Skip to content

Commit b1edc8a

Browse files
Merge branch 'support-slds-2' into support-slds-2-picklist
2 parents de94337 + 7ee9d46 commit b1edc8a

File tree

8 files changed

+112
-69
lines changed

8 files changed

+112
-69
lines changed

src/scripts/DateInput.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ export const DateInput = createFC<DateInputProps, { isFormElement: boolean }>(
322322
});
323323

324324
const formElemProps = {
325-
id,
325+
controlId: id,
326326
cols,
327327
label,
328328
required,
@@ -333,7 +333,16 @@ export const DateInput = createFC<DateInputProps, { isFormElement: boolean }>(
333333
};
334334
return (
335335
<FormElement {...formElemProps}>
336-
<div className={classnames(className, 'slds-dropdown-trigger')}>
336+
<div
337+
className={classnames(
338+
className,
339+
'slds-dropdown-trigger',
340+
'slds-dropdown-trigger_click',
341+
{
342+
'slds-is-open': opened,
343+
}
344+
)}
345+
>
337346
<div className='slds-input-has-icon slds-input-has-icon_right'>
338347
<Input
339348
inputRef={inputRef}

src/scripts/Datepicker.tsx

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -141,39 +141,44 @@ const DatepickerFilter: FC<DatepickerFilterProps> = (props) => {
141141
const onNextMonth = useEventCallback(() => onMonthChange(1));
142142
return (
143143
<div className='slds-datepicker__filter slds-grid'>
144-
<div className='slds-datepicker__filter_month slds-grid slds-grid_align-spread slds-size_2-of-3'>
144+
<div className='slds-datepicker__filter_month slds-grid slds-grid_align-spread slds-grow'>
145145
<div className='slds-align-middle'>
146146
<Button
147-
className='slds-align-middle'
148147
type='icon-container'
149148
icon='left'
150-
size='small'
151149
alt='Previous Month'
152150
onClick={onPrevMonth}
153151
/>
154152
</div>
155-
<h2 className='slds-align-middle'>{dayjs.monthsShort()[cal.month]}</h2>
153+
<h2
154+
className='slds-align-middle'
155+
aria-atomic='false'
156+
aria-live='polite'
157+
>
158+
{dayjs.monthsShort()[cal.month]}
159+
</h2>
156160
<div className='slds-align-middle'>
157161
<Button
158-
className='slds-align-middle'
159162
type='icon-container'
160163
icon='right'
161-
size='small'
162164
alt='Next Month'
163165
onClick={onNextMonth}
164166
/>
165167
</div>
166168
</div>
167-
<div className='slds-size_1-of-3'>
168-
<Select value={cal.year} onChange={onYearChange}>
169-
{new Array(11)
170-
.join('_')
171-
.split('_')
172-
.map((a, i) => {
173-
const year = cal.year + i - 5;
174-
return <Option key={year} label={String(year)} value={year} />;
175-
})}
176-
</Select>
169+
<div className='slds-shrink-none'>
170+
<label className='slds-assistive-text'>Pick a Year</label>
171+
<div className='slds-select_container'>
172+
<Select value={cal.year} onChange={onYearChange}>
173+
{new Array(11)
174+
.join('_')
175+
.split('_')
176+
.map((a, i) => {
177+
const year = cal.year + i - 5;
178+
return <Option key={year} label={String(year)} value={year} />;
179+
})}
180+
</Select>
181+
</div>
177182
</div>
178183
</div>
179184
);
@@ -196,7 +201,6 @@ type DatepickerDateProps = {
196201
selectedDate: string | undefined;
197202
today: string;
198203
date: CalendarDate;
199-
dayIndex: number;
200204
} & DatepickerHandlers;
201205

202206
/**
@@ -208,7 +212,6 @@ const DatepickerDate: FC<DatepickerDateProps> = (props) => {
208212
selectedDate,
209213
today,
210214
date,
211-
dayIndex,
212215
onDateKeyDown: onDateKeyDown_,
213216
onDateClick: onDateClick_,
214217
onDateFocus: onDateFocus_,
@@ -241,18 +244,20 @@ const DatepickerDate: FC<DatepickerDateProps> = (props) => {
241244
}
242245
const selected = date.value === selectedDate;
243246
const isToday = date.value === today;
247+
const isAdjacentMonth = date.month !== cal.month;
244248
const dateClassName = classnames({
245249
'slds-disabled-text': !enabled,
246250
'slds-is-selected': selected,
247251
'slds-is-today': isToday,
252+
'slds-day_adjacent-month': isAdjacentMonth,
248253
});
249254
return (
250255
<td
251256
className={dateClassName}
252-
headers={dayjs().weekday(dayIndex).format('ddd')}
253257
role='gridcell'
254-
aria-disabled={!enabled}
255258
aria-selected={selected}
259+
aria-current={isToday ? 'date' : undefined}
260+
aria-label={dayjs(date.value).format('D MMMM YYYY')}
256261
>
257262
<span
258263
className='slds-day'
@@ -292,12 +297,7 @@ const DatepickerMonth = forwardRef(
292297
onDateKeyDown,
293298
} = props;
294299
return (
295-
<table
296-
ref={ref}
297-
className='datepicker__month'
298-
role='grid'
299-
aria-labelledby='month'
300-
>
300+
<table ref={ref} className='slds-datepicker__month' role='grid'>
301301
<thead>
302302
<tr>
303303
{dayjs.weekdaysMin(true).map((wd, i) => (
@@ -489,6 +489,7 @@ export const Datepicker: FC<DatepickerProps> = (props) => {
489489
className={datepickerClassNames}
490490
ref={elementRef}
491491
tabIndex={-1}
492+
role='dialog'
492493
aria-hidden={false}
493494
onBlur={onBlur}
494495
onKeyDown={onKeyDown}

src/scripts/Input.tsx

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import React, {
2+
useId,
23
ReactElement,
34
InputHTMLAttributes,
45
KeyboardEvent,
@@ -155,32 +156,37 @@ export const Input = createFC<InputProps, { isFormElement: boolean }>(
155156
prevValueRef.current = e.target.value;
156157
});
157158

159+
const prefix = useId();
160+
161+
const rawTextId = id ?? `${prefix}-raw-text-id`;
162+
const inputId = id ?? `${prefix}-input-id`;
163+
const labelForId = readOnly ? rawTextId : inputId;
164+
165+
const errorId = `${prefix}-error-id`;
166+
158167
const { isFieldSetColumn } = useContext(FieldSetColumnContext);
159168
const inputClassNames = classnames(
160169
className,
161170
bare ? 'slds-input_bare' : 'slds-input'
162171
);
163172
const inputElem = readOnly ? (
164-
<Text
165-
id={id}
166-
type='regular'
167-
category='body'
168-
className='slds-form-element__static'
169-
>
173+
<Text id={rawTextId} type='regular' category='body'>
170174
{value}
171175
</Text>
172176
) : (
173177
<input
174178
ref={inputRef}
175179
className={inputClassNames}
176-
id={id}
180+
id={inputId}
177181
type={type}
178182
value={value}
179183
defaultValue={defaultValue}
180184
readOnly={htmlReadOnly}
181185
{...rprops}
182186
onChange={onChange}
183187
onKeyDown={onKeyDown}
188+
aria-describedby={error ? errorId : undefined}
189+
aria-invalid={error ? true : undefined}
184190
/>
185191
);
186192

@@ -206,10 +212,11 @@ export const Input = createFC<InputProps, { isFormElement: boolean }>(
206212
}
207213
if (isFieldSetColumn || label || required || error || cols) {
208214
const formElemProps = {
209-
id,
215+
controlId: labelForId,
210216
label,
211217
required,
212218
error,
219+
errorId,
213220
readOnly,
214221
cols,
215222
tooltip,

src/scripts/Tabs.tsx

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,18 +47,16 @@ const TabsContext = createContext<{
4747
type: TabType;
4848
activeTabRef?: Ref<HTMLAnchorElement>;
4949
tabIdPrefix?: string;
50-
tabItemIdPrefix?: string;
5150
}>({ type: 'default' });
5251

5352
/**
5453
* Custom hook to generate unique tab IDs
5554
*/
5655
const useTabIds = (eventKey?: TabKey) => {
57-
const { tabIdPrefix, tabItemIdPrefix } = useContext(TabsContext);
56+
const { tabIdPrefix } = useContext(TabsContext);
5857
const tabIndex = eventKey ? String(eventKey) : '0';
5958
const tabId = `${tabIdPrefix}-${tabIndex}`;
60-
const tabItemId = `${tabItemIdPrefix}-${tabIndex}`;
61-
return { tabId, tabItemId };
59+
return { tabId };
6260
};
6361

6462
/**
@@ -158,7 +156,7 @@ const TabItem = <RendererProps extends TabItemRendererProps>(
158156
const { type, activeTabRef } = useContext(TabsContext);
159157
const activeKey = useContext(TabsActiveKeyContext);
160158
const { onTabClick, onTabKeyDown } = useContext(TabsHandlersContext);
161-
const { tabId, tabItemId } = useTabIds(eventKey);
159+
const { tabId } = useTabIds(eventKey);
162160
let { menuItems } = props;
163161
menuItems = menu
164162
? React.Children.toArray(
@@ -197,7 +195,6 @@ const TabItem = <RendererProps extends TabItemRendererProps>(
197195
}`}
198196
>
199197
<a
200-
id={tabItemId}
201198
className={tabLinkClassName}
202199
role='tab'
203200
ref={isActive ? activeTabRef : undefined}
@@ -262,13 +259,12 @@ export const Tab = <
262259
) => {
263260
const { className, eventKey, children } = props;
264261
const activeKey = useContext(TabsActiveKeyContext);
265-
const { tabId, tabItemId } = useTabIds(eventKey);
262+
const { tabId } = useTabIds(eventKey);
266263
return (
267264
<TabContent
268265
id={tabId}
269266
className={className}
270267
active={eventKey != null && eventKey === activeKey}
271-
aria-labelledby={tabItemId}
272268
>
273269
{children}
274270
</TabContent>

src/scripts/Textarea.tsx

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import React, {
2+
useId,
23
ChangeEvent,
34
ReactNode,
45
Ref,
@@ -7,6 +8,7 @@ import React, {
78
useRef,
89
} from 'react';
910
import classnames from 'classnames';
11+
import { Text } from './Text';
1012
import { FormElement, FormElementProps } from './FormElement';
1113
import { FieldSetColumnContext } from './FieldSet';
1214
import { useEventCallback } from './hooks';
@@ -25,6 +27,8 @@ export type TextareaProps = {
2527
elementRef?: Ref<HTMLDivElement>;
2628
textareaRef?: Ref<HTMLTextAreaElement>;
2729
onValueChange?: (value: string, prevValue?: string) => void;
30+
readOnly?: boolean;
31+
htmlReadOnly?: boolean;
2832
} & TextareaHTMLAttributes<HTMLTextAreaElement>;
2933

3034
/**
@@ -45,6 +49,8 @@ export const Textarea = createFC<TextareaProps, { isFormElement: boolean }>(
4549
textareaRef,
4650
onChange: onChange_,
4751
onValueChange,
52+
readOnly,
53+
htmlReadOnly,
4854
...rprops
4955
} = props;
5056
const prevValueRef = useRef<string>();
@@ -54,26 +60,40 @@ export const Textarea = createFC<TextareaProps, { isFormElement: boolean }>(
5460
prevValueRef.current = e.target.value;
5561
});
5662
const { isFieldSetColumn } = useContext(FieldSetColumnContext);
57-
const taClassNames = classnames(className, 'slds-input');
58-
const textareaElem = (
63+
const errorId = useId();
64+
const taClassNames = classnames(className, 'slds-textarea');
65+
const textareaElem = readOnly ? (
66+
<Text
67+
id={id}
68+
type='regular'
69+
category='body'
70+
className='slds-form-element__static'
71+
>
72+
{rprops.value}
73+
</Text>
74+
) : (
5975
<textarea
6076
id={id}
6177
ref={textareaRef}
6278
className={taClassNames}
79+
readOnly={htmlReadOnly}
6380
{...rprops}
6481
onChange={onChange}
82+
aria-describedby={error ? errorId : undefined}
6583
/>
6684
);
6785
if (isFieldSetColumn || label || required || error || cols) {
6886
const formElemProps = {
69-
id,
87+
controlId: id,
7088
label,
7189
required,
7290
error,
91+
errorId,
7392
cols,
7493
tooltip,
7594
tooltipIcon,
7695
elementRef,
96+
readOnly,
7797
};
7898
return <FormElement {...formElemProps}>{textareaElem}</FormElement>;
7999
}

src/scripts/Toggle.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,20 +49,25 @@ export const Toggle = createFC<ToggleProps, { isFormElement: boolean }>(
4949
<input
5050
ref={inputRef}
5151
id={id}
52-
name='checkbox'
5352
type='checkbox'
54-
aria-describedby='toggle-desc'
5553
{...rprops}
5654
onChange={onChange}
5755
/>
58-
<span className='slds-checkbox_faux_container' aria-live='assertive'>
56+
<span className='slds-checkbox_faux_container'>
5957
<span className='slds-checkbox_faux' />
6058
<span className='slds-checkbox_on'>Enabled</span>
6159
<span className='slds-checkbox_off'>Disabled</span>
6260
</span>
6361
</label>
6462
);
65-
const formElemProps = { id, label, required, error, cols, elementRef };
63+
const formElemProps = {
64+
controlId: id,
65+
label,
66+
required,
67+
error,
68+
cols,
69+
elementRef,
70+
};
6671
return <FormElement {...formElemProps}>{toggle}</FormElement>;
6772
},
6873
{ isFormElement: true }

0 commit comments

Comments
 (0)