Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(listitembase): dynamic list support #600

Closed
wants to merge 36 commits into from
Closed
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
bd6d2e0
feat(listitembase): allow empty items
Coread Aug 30, 2024
a456235
feat(list): tests for list item base
Coread Sep 2, 2024
8a7be16
feat(listitembase): prevent initial autofocus
Coread Sep 2, 2024
3c0f5d7
test(aria-toolbar): update snapshot
Coread Sep 3, 2024
e6ad993
feat(list-item): add full list of dependencies to useEffect
Coread Sep 3, 2024
0063601
feat(list-item): make empty elements aria-hidden
Coread Sep 3, 2024
34698aa
feat(list-item): focusChild
Coread Sep 3, 2024
7d6fe38
feat(list): remove unused import
Coread Sep 3, 2024
2a14e38
Merge branch 'master' of github.com:momentum-design/momentum-react-v2…
Coread Sep 3, 2024
9574861
feat(listitem): initial focus
Coread Sep 6, 2024
11ea311
feat(listitem): remove unused import
Coread Sep 6, 2024
b0b9218
Merge branch 'master' of github.com:momentum-design/momentum-react-v2…
Coread Sep 6, 2024
2fd6364
feat(listitem): focus within handling
Coread Sep 9, 2024
eabcb5f
feat(list): don't update initialFocus while focused
Coread Sep 9, 2024
70e7105
feat(list): dynamic list update
Coread Sep 12, 2024
9dc5a80
feat(listitem): fix text selection and press events
Coread Sep 13, 2024
cdf6616
fix(cursor): text selection
jor-row Sep 13, 2024
ce97696
fix(meeting-list-item): update tests and stories
Coread Sep 14, 2024
779ac9f
Merge branch 'allow_empty_list_items' of github.com:momentum-design/m…
Coread Sep 14, 2024
57721d2
feat(listitembase): remove handling for empty list items
Coread Sep 20, 2024
e8cbc5f
feat(listitembase): change to updateFocusBlocked
Coread Sep 20, 2024
802a71f
Merge branch 'master' of github.com:momentum-design/momentum-react-v2…
Coread Sep 21, 2024
7596254
feat(list-item): remove allowExclusions
Coread Sep 22, 2024
04d48fc
fix(list): supress incorrect lint warning
Coread Sep 22, 2024
bef0141
feat(hooks): simplify focus hooks
Coread Sep 22, 2024
16a39b8
feat(listitembase): extend types instead of redefining
Coread Sep 22, 2024
19bbbb3
chore: merge latest master
jor-row Sep 26, 2024
16be115
fix(tests): unit and snapshots
jor-row Sep 30, 2024
6b46bd8
Merge remote-tracking branch 'upstream/master' into allow_empty_list_…
jor-row Sep 30, 2024
3f94da1
fix(tree): unit tests
jor-row Sep 30, 2024
bf44346
fix(tree): ignore deps in useEffects
Coread Oct 1, 2024
b89da7c
fix(menu): ignore exhaustive deps
Coread Oct 1, 2024
dae3c7b
Merge branch 'master' of github.com:momentum-design/momentum-react-v2…
Coread Oct 1, 2024
5de7adf
feat(button-group): fix type
Coread Oct 2, 2024
8ac511e
fix(pr): comments
jor-row Oct 3, 2024
e26ed13
feat(list): remove unused supressFocus
Coread Oct 3, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .eslintrc.js
jor-row marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ module.exports = {
'plugin:storybook/recommended',
'prettier',
],
plugins: ['react', 'jsx-a11y', '@typescript-eslint'],
plugins: ['react', 'react-hooks', 'jsx-a11y', '@typescript-eslint'],
ignorePatterns: ['**/src/app', '**/es', '**/dist', '**/docs', '**/*.d.ts'],
parser: '@typescript-eslint/parser',
parserOptions: {
Expand Down Expand Up @@ -86,6 +86,7 @@ module.exports = {
forbid: ['any'],
},
],
'react-hooks/exhaustive-deps': 'warn',
'react/jsx-boolean-value': 0,
'react/jsx-closing-bracket-location': 0,
'react/jsx-curly-spacing': 1,
Expand Down
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@
"react": ">=16.14.0",
"react-dom": ">=16.14.0"
},
"resolutions": {
"@react-aria/button": "3.4.3"
},
"dependencies": {
"@babel/runtime": "^7.0.0",
"@momentum-design/animations": "^0.0.4",
Expand Down Expand Up @@ -204,6 +207,7 @@
"eslint-plugin-import": "^2.23.4",
"eslint-plugin-jsx-a11y": "^6.1.2",
"eslint-plugin-react": "7.12.2",
"eslint-plugin-react-hooks": "1.7.0",
"eslint-plugin-storybook": "^0.5.5",
"eslint-watch": "^6.0.0",
"file-loader": "^1.1.6",
Expand Down
22 changes: 20 additions & 2 deletions src/components/AriaToolbar/AriaToolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { DEFAULTS, STYLE } from './AriaToolbar.constants';
import { Props } from './AriaToolbar.types';
import ButtonGroup from '../ButtonGroup';
import { AriaToolbarContext } from './AriaToolbar.utils';
import { getKeyboardFocusableElements } from '../../utils/navigation';

/**
* The AriaToolbar component. A style-less by default or button-group styled component implementing the Aria Toolbar pattern
Expand All @@ -29,6 +30,7 @@ const AriaToolbar: FC<Props> = (props: Props) => {
const [currentFocus, setCurrentFocus] = useState(undefined);

const buttonRefs = useRef({});
const ref = useRef<HTMLDivElement>(null);

useEffect(() => {
buttonRefs.current[currentFocus]?.focus();
Expand Down Expand Up @@ -59,15 +61,31 @@ const AriaToolbar: FC<Props> = (props: Props) => {
const renderBody = () => {
if (shouldRenderAsButtonGroup) {
return (
<ButtonGroup {...buttonGroupProps} {...commonProps} orientation={orientation}>
<ButtonGroup ref={ref} {...buttonGroupProps} {...commonProps} orientation={orientation}>
{children}
</ButtonGroup>
);
} else {
return <div {...commonProps}>{children}</div>;
return (
<div ref={ref} {...commonProps}>
{children}
</div>
);
}
};

useEffect(() => {
// When the toolbar is rendered inside a list, only item in the toolbar
jor-row marked this conversation as resolved.
Show resolved Hide resolved
// should be focusable. This is the preserve the tab order as the
// List uses a roving tab index.
getKeyboardFocusableElements(ref.current, false).forEach((el, index) => {
if (index === 0) {
return;
}
el.setAttribute('data-exclude-focus', 'true');
});
}, [ref]);

return (
<AriaToolbarContext.Provider value={getContext()}>{renderBody()}</AriaToolbarContext.Provider>
);
Expand Down
8 changes: 8 additions & 0 deletions src/components/AriaToolbar/AriaToolbar.unit.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -1154,11 +1154,13 @@ exports[`<AriaToolbar /> snapshot should update order if triggered externally 1`
/>
<button
class="md-button-simple-wrapper"
data-exclude-focus="true"
tabindex="-1"
type="button"
/>
<button
class="md-button-simple-wrapper"
data-exclude-focus="true"
tabindex="-1"
type="button"
/>
Expand All @@ -1180,11 +1182,13 @@ exports[`<AriaToolbar /> snapshot should update order if triggered externally 2`
/>
<button
class="md-button-simple-wrapper"
data-exclude-focus="true"
tabindex="-1"
type="button"
/>
<button
class="md-button-simple-wrapper md-focus-ring-wrapper children"
data-exclude-focus="true"
type="button"
/>
</div>
Expand All @@ -1205,10 +1209,12 @@ exports[`<AriaToolbar /> snapshot should update order if triggered externally 3`
/>
<button
class="md-button-simple-wrapper md-focus-ring-wrapper children"
data-exclude-focus="true"
type="button"
/>
<button
class="md-button-simple-wrapper"
data-exclude-focus="true"
tabindex="-1"
type="button"
/>
Expand All @@ -1230,11 +1236,13 @@ exports[`<AriaToolbar /> snapshot should update order if triggered externally 4`
/>
<button
class="md-button-simple-wrapper"
data-exclude-focus="true"
tabindex="-1"
type="button"
/>
<button
class="md-button-simple-wrapper md-focus-ring-wrapper children"
data-exclude-focus="true"
type="button"
/>
</div>
Expand Down
20 changes: 17 additions & 3 deletions src/components/AvatarMeetingsListItem/AvatarMeetingsListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,30 @@ const AvatarMeetingsListItem: FC<Props> = (props: Props) => {
switch (schedulerState) {
case SCHEDULER_STATES.available:
return (
<Icon name="scheduler-available" fillColor="var(--mds-color-theme-text-success-normal)" {...iconProps} />
<Icon
name="scheduler-available"
fillColor="var(--mds-color-theme-text-success-normal)"
{...iconProps}
/>
);

case SCHEDULER_STATES.unavailable:
return (
<Icon name="scheduler-unavailable" fillColor="var(--mds-color-theme-text-warning-normal)" {...iconProps} />
<Icon
name="scheduler-unavailable"
fillColor="var(--mds-color-theme-text-warning-normal)"
{...iconProps}
/>
);

case SCHEDULER_STATES.unknown:
return <Icon name="scheduler-unknown" fillColor="var(--mds-color-theme-text-error-normal)" {...iconProps} />;
return (
<Icon
name="scheduler-unknown"
fillColor="var(--mds-color-theme-text-error-normal)"
{...iconProps}
/>
);

case SCHEDULER_STATES.quietHours:
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ exports[`<AvatarMeetingsListItem /> snapshot should match snapshot 1`] = `
onMouseEnter={[Function]}
onMouseLeave={[Function]}
onMouseUp={[Function]}
onPointerDown={[Function]}
onTouchCancel={[Function]}
onTouchEnd={[Function]}
onTouchMove={[Function]}
Expand Down Expand Up @@ -156,6 +157,7 @@ exports[`<AvatarMeetingsListItem /> snapshot should match snapshot with avatarPr
onMouseEnter={[Function]}
onMouseLeave={[Function]}
onMouseUp={[Function]}
onPointerDown={[Function]}
onTouchCancel={[Function]}
onTouchEnd={[Function]}
onTouchMove={[Function]}
Expand Down Expand Up @@ -314,6 +316,7 @@ exports[`<AvatarMeetingsListItem /> snapshot should match snapshot with classNam
onMouseEnter={[Function]}
onMouseLeave={[Function]}
onMouseUp={[Function]}
onPointerDown={[Function]}
onTouchCancel={[Function]}
onTouchEnd={[Function]}
onTouchMove={[Function]}
Expand Down Expand Up @@ -433,6 +436,7 @@ exports[`<AvatarMeetingsListItem /> snapshot should match snapshot with displayM
onMouseEnter={[Function]}
onMouseLeave={[Function]}
onMouseUp={[Function]}
onPointerDown={[Function]}
onTouchCancel={[Function]}
onTouchEnd={[Function]}
onTouchMove={[Function]}
Expand Down Expand Up @@ -630,6 +634,7 @@ exports[`<AvatarMeetingsListItem /> snapshot should match snapshot with displayM
onMouseEnter={[Function]}
onMouseLeave={[Function]}
onMouseUp={[Function]}
onPointerDown={[Function]}
onTouchCancel={[Function]}
onTouchEnd={[Function]}
onTouchMove={[Function]}
Expand Down Expand Up @@ -832,6 +837,7 @@ exports[`<AvatarMeetingsListItem /> snapshot should match snapshot with displayM
onMouseEnter={[Function]}
onMouseLeave={[Function]}
onMouseUp={[Function]}
onPointerDown={[Function]}
onTouchCancel={[Function]}
onTouchEnd={[Function]}
onTouchMove={[Function]}
Expand Down Expand Up @@ -1030,6 +1036,7 @@ exports[`<AvatarMeetingsListItem /> snapshot should match snapshot with firstLin
onMouseEnter={[Function]}
onMouseLeave={[Function]}
onMouseUp={[Function]}
onPointerDown={[Function]}
onTouchCancel={[Function]}
onTouchEnd={[Function]}
onTouchMove={[Function]}
Expand Down Expand Up @@ -1149,6 +1156,7 @@ exports[`<AvatarMeetingsListItem /> snapshot should match snapshot with id 1`] =
onMouseEnter={[Function]}
onMouseLeave={[Function]}
onMouseUp={[Function]}
onPointerDown={[Function]}
onTouchCancel={[Function]}
onTouchEnd={[Function]}
onTouchMove={[Function]}
Expand Down Expand Up @@ -1264,6 +1272,7 @@ exports[`<AvatarMeetingsListItem /> snapshot should match snapshot with schedule
onMouseEnter={[Function]}
onMouseLeave={[Function]}
onMouseUp={[Function]}
onPointerDown={[Function]}
onTouchCancel={[Function]}
onTouchEnd={[Function]}
onTouchMove={[Function]}
Expand Down Expand Up @@ -1409,6 +1418,7 @@ exports[`<AvatarMeetingsListItem /> snapshot should match snapshot with schedule
onMouseEnter={[Function]}
onMouseLeave={[Function]}
onMouseUp={[Function]}
onPointerDown={[Function]}
onTouchCancel={[Function]}
onTouchEnd={[Function]}
onTouchMove={[Function]}
Expand Down Expand Up @@ -1554,6 +1564,7 @@ exports[`<AvatarMeetingsListItem /> snapshot should match snapshot with schedule
onMouseEnter={[Function]}
onMouseLeave={[Function]}
onMouseUp={[Function]}
onPointerDown={[Function]}
onTouchCancel={[Function]}
onTouchEnd={[Function]}
onTouchMove={[Function]}
Expand Down Expand Up @@ -1699,6 +1710,7 @@ exports[`<AvatarMeetingsListItem /> snapshot should match snapshot with schedule
onMouseEnter={[Function]}
onMouseLeave={[Function]}
onMouseUp={[Function]}
onPointerDown={[Function]}
onTouchCancel={[Function]}
onTouchEnd={[Function]}
onTouchMove={[Function]}
Expand Down Expand Up @@ -1844,6 +1856,7 @@ exports[`<AvatarMeetingsListItem /> snapshot should match snapshot with secondLi
onMouseEnter={[Function]}
onMouseLeave={[Function]}
onMouseUp={[Function]}
onPointerDown={[Function]}
onTouchCancel={[Function]}
onTouchEnd={[Function]}
onTouchMove={[Function]}
Expand Down Expand Up @@ -1978,6 +1991,7 @@ exports[`<AvatarMeetingsListItem /> snapshot should match snapshot with style 1`
onMouseEnter={[Function]}
onMouseLeave={[Function]}
onMouseUp={[Function]}
onPointerDown={[Function]}
onTouchCancel={[Function]}
onTouchEnd={[Function]}
onTouchMove={[Function]}
Expand Down
12 changes: 8 additions & 4 deletions src/components/ButtonGroup/ButtonGroup.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { FC } from 'react';
import React, { forwardRef, RefObject } from 'react';
import classNames from 'classnames';

import { DEFAULTS, STYLE, CHILD_OF } from './ButtonGroup.constants';
Expand All @@ -7,12 +7,13 @@ import { Props } from './ButtonGroup.types';
import './ButtonGroup.style.scss';

export interface CompoundProps {
CHILD_PROPS: {
CHILD_PROPS?: {
[CHILD_OF.KEY]: typeof CHILD_OF.VALUE;
};
}

const ButtonGroup: FC<Props> & CompoundProps = (props: Props) => {
const ButtonGroup: React.ForwardRefExoticComponent<Props & React.RefAttributes<HTMLDivElement>> &
CompoundProps = forwardRef((props: Props, ref: RefObject<HTMLDivElement>) => {
const {
children,
className,
Expand All @@ -29,6 +30,7 @@ const ButtonGroup: FC<Props> & CompoundProps = (props: Props) => {

return (
<div
ref={ref}
className={classNames(STYLE.wrapper, className)}
data-round={round || DEFAULTS.ROUND}
data-spaced={spaced || DEFAULTS.SPACED}
Expand All @@ -43,7 +45,9 @@ const ButtonGroup: FC<Props> & CompoundProps = (props: Props) => {
{children}
</div>
);
};
});

ButtonGroup.displayName = 'ButtonGroup';

export default ButtonGroup;

Expand Down
12 changes: 9 additions & 3 deletions src/components/Icon/Icon.unit.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,9 @@ describe('<Icon />', () => {

const ariaLabel = 'This participant is muted';

container = await mountAndWait(<Icon name="draft-indicator" weight="bold" ariaLabel={ariaLabel} />);
container = await mountAndWait(
<Icon name="draft-indicator" weight="bold" ariaLabel={ariaLabel} />
);

expect(container).toMatchSnapshot();
});
Expand All @@ -72,7 +74,9 @@ describe('<Icon />', () => {

const ariaLabel = 'This participant is muted';

container = await mountAndWait(<Icon name="draft-indicator" weight="bold" aria-label={ariaLabel} />);
container = await mountAndWait(
<Icon name="draft-indicator" weight="bold" aria-label={ariaLabel} />
);

expect(container).toMatchSnapshot();
});
Expand Down Expand Up @@ -205,7 +209,9 @@ describe('<Icon />', () => {

const title = 'You have a draft message';

const wrapper = await mountAndWait(<Icon name="draft-indicator" weight="bold" title={title} />);
const wrapper = await mountAndWait(
<Icon name="draft-indicator" weight="bold" title={title} />
);
const element = wrapper.find(Icon).getDOMNode();

expect(element.getAttribute('title')).toBe(title);
Expand Down
4 changes: 3 additions & 1 deletion src/components/Icon/Icon.utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ describe('getResolvedSVGName', () => {
['icon-name', 'thin', true, 'icon-name'],
['icon-name', 'filled', true, 'icon-name'],
])('returns the correct name for %s, %s, %s', (name, weight, weightless, expected) => {
expect(getResolvedSVGName(name as InferredIconName, weight as IconWeight, weightless)).toEqual(expected);
expect(getResolvedSVGName(name as InferredIconName, weight as IconWeight, weightless)).toEqual(
expected
);
});
});
7 changes: 5 additions & 2 deletions src/components/Icon/Icon.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,8 @@ import { DEFAULTS } from './Icon.constants';
* @param weightless - marks that this icon doesn't have a weight
* @returns - resolved name of the icon
*/
export const getResolvedSVGName = (name: InferredIconName, weight: IconWeight, weightless: boolean): string =>
weightless ? `${String(name)}` : `${String(name)}-${weight || DEFAULTS.WEIGHT}`;
export const getResolvedSVGName = (
name: InferredIconName,
weight: IconWeight,
weightless: boolean
): string => (weightless ? `${String(name)}` : `${String(name)}-${weight || DEFAULTS.WEIGHT}`);
1 change: 1 addition & 0 deletions src/components/List/List.constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const CLASS_PREFIX = 'md-list';

const DEFAULTS = {
ORIENTATION: 'vertical' as ListOrientation,
INITIAL_FOCUS: 0,
};

const STYLE = {
Expand Down
Loading
Loading