Skip to content

Commit

Permalink
Tag/TagButton init component setup (#27102)
Browse files Browse the repository at this point in the history
* add raw html

* add types and update render to use slots

* init slots and layout setup for tag

* init pass on Tag button

* fix dep version

* add icons to package.json

* api update

* update tests

* Update packages/react-components/react-tags/src/components/Tag/Tag.types.ts

Co-authored-by: Esteban Munoz Facusse <[email protected]>

* Update packages/react-components/react-tags/src/components/Tag/useTag.tsx

Co-authored-by: Esteban Munoz Facusse <[email protected]>

* Update packages/react-components/react-tags/src/components/TagButton/TagButton.types.ts

Co-authored-by: Esteban Munoz Facusse <[email protected]>

* Update packages/react-components/react-tags/src/components/TagButton/renderTagButton.tsx

Co-authored-by: Esteban Munoz Facusse <[email protected]>

* remove unnecessary named columns

* Update packages/react-components/react-tags/src/components/Tag/useTag.tsx

Co-authored-by: Sean Monahan <[email protected]>

* Update packages/react-components/react-tags/src/components/TagButton/useTagButton.tsx

Co-authored-by: Sean Monahan <[email protected]>

* bmp react utils to match master

* Update packages/react-components/react-tags/package.json

* Update api.md

* Update test snapshot with react-icon update

---------

Co-authored-by: Esteban Munoz Facusse <[email protected]>
Co-authored-by: Sean Monahan <[email protected]>
Co-authored-by: Amber <[email protected]>
Co-authored-by: Amber <[email protected]>
  • Loading branch information
5 people authored Apr 27, 2023
1 parent 1998266 commit a3edb5d
Show file tree
Hide file tree
Showing 18 changed files with 503 additions and 94 deletions.
41 changes: 35 additions & 6 deletions packages/react-components/react-tags/etc/react-tags.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
```ts

/// <reference types="react" />

import { Avatar } from '@fluentui/react-avatar';
import type { ComponentProps } from '@fluentui/react-utilities';
import type { ComponentState } from '@fluentui/react-utilities';
import type { ForwardRefComponent } from '@fluentui/react-utilities';
Expand All @@ -27,29 +30,55 @@ export const TagButton: ForwardRefComponent<TagButtonProps>;
export const tagButtonClassNames: SlotClassNames<TagButtonSlots>;

// @public
export type TagButtonProps = ComponentProps<TagButtonSlots> & {};
export type TagButtonProps = ComponentProps<TagButtonSlots> & {
size?: 'extra-small' | 'small' | 'medium';
shape?: 'rounded' | 'circular';
appearance?: 'filled-darker' | 'filled-lighter' | 'tint' | 'outline';
disabled?: boolean;
checked?: boolean;
dismissable?: boolean;
};

// @public (undocumented)
export type TagButtonSlots = {
root: Slot<'div'>;
root: NonNullable<Slot<'div'>>;
contentButton?: Slot<'button'>;
avatar?: Slot<typeof Avatar>;
icon?: Slot<'span'>;
primaryText?: Slot<'span'>;
secondaryText?: Slot<'span'>;
dismissButton?: NonNullable<Slot<'button'>>;
};

// @public
export type TagButtonState = ComponentState<TagButtonSlots>;
export type TagButtonState = ComponentState<TagButtonSlots> & Required<Pick<TagButtonProps, 'appearance' | 'checked' | 'disabled' | 'dismissable' | 'shape' | 'size'>>;

// @public (undocumented)
export const tagClassNames: SlotClassNames<TagSlots>;

// @public
export type TagProps = ComponentProps<TagSlots> & {};
export type TagProps = ComponentProps<TagSlots> & {
size?: 'extra-small' | 'small' | 'medium';
shape?: 'rounded' | 'circular';
appearance?: 'filled-darker' | 'filled-lighter' | 'tint' | 'outline';
disabled?: boolean;
checked?: boolean;
dismissable?: boolean;
};

// @public (undocumented)
export type TagSlots = {
root: Slot<'div'>;
root: NonNullable<Slot<'div'>>;
content?: Slot<'span'>;
avatar?: Slot<typeof Avatar>;
icon?: Slot<'span'>;
primaryText?: Slot<'span'>;
secondaryText?: Slot<'span'>;
dismissButton?: NonNullable<Slot<'button'>>;
};

// @public
export type TagState = ComponentState<TagSlots>;
export type TagState = ComponentState<TagSlots> & Required<Pick<TagProps, 'appearance' | 'checked' | 'disabled' | 'dismissable' | 'shape' | 'size'>>;

// @public
export const useTag_unstable: (props: TagProps, ref: React_2.Ref<HTMLElement>) => TagState;
Expand Down
2 changes: 2 additions & 0 deletions packages/react-components/react-tags/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
"@fluentui/scripts-tasks": "*"
},
"dependencies": {
"@fluentui/react-avatar": "^9.4.10",
"@fluentui/react-icons": "^2.0.196",
"@fluentui/react-jsx-runtime": "9.0.0-alpha.2",
"@fluentui/react-theme": "^9.1.7",
"@fluentui/react-utilities": "^9.8.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,27 @@ import { render } from '@testing-library/react';
import { Tag } from './Tag';
import { isConformant } from '../../testing/isConformant';

const requiredProps = {
avatar: {
name: 'Katri Athokas',
},
icon: 'i',
primaryText: 'Primary text',
secondaryText: 'Secondary text',
dismissable: true,
};

describe('Tag', () => {
isConformant({
Component: Tag,
displayName: 'Tag',
requiredProps,
});

// TODO add more tests here, and create visual regression tests in /apps/vr-tests

it('renders a default state', () => {
const result = render(<Tag>Default Tag</Tag>);
const result = render(<Tag {...requiredProps}>Default Tag</Tag>);
expect(result.container).toMatchSnapshot();
});
});
Original file line number Diff line number Diff line change
@@ -1,17 +1,30 @@
import type { ComponentProps, ComponentState, Slot } from '@fluentui/react-utilities';
import { Avatar } from '@fluentui/react-avatar';

export type TagSlots = {
root: Slot<'div'>;
root: NonNullable<Slot<'div'>>;
content?: Slot<'span'>;
avatar?: Slot<typeof Avatar>;
icon?: Slot<'span'>;
primaryText?: Slot<'span'>;
secondaryText?: Slot<'span'>;
dismissButton?: NonNullable<Slot<'button'>>;
};

/**
* Tag Props
*/
export type TagProps = ComponentProps<TagSlots> & {};
export type TagProps = ComponentProps<TagSlots> & {
size?: 'extra-small' | 'small' | 'medium';
shape?: 'rounded' | 'circular';
appearance?: 'filled-darker' | 'filled-lighter' | 'tint' | 'outline';
disabled?: boolean;
checked?: boolean;
dismissable?: boolean;
};

/**
* State used in rendering Tag
*/
export type TagState = ComponentState<TagSlots>;
// TODO: Remove semicolon from previous line, uncomment next line, and provide union of props to pick from TagProps.
// & Required<Pick<TagProps, 'propName'>>
export type TagState = ComponentState<TagSlots> &
Required<Pick<TagProps, 'appearance' | 'checked' | 'disabled' | 'dismissable' | 'shape' | 'size'>>;
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,57 @@ exports[`Tag renders a default state 1`] = `
<div
class="fui-Tag"
>
Default Tag
<span
class="fui-Tag__content"
>
<span
aria-label="Katri Athokas"
class="fui-Avatar fui-Tag__avatar"
id="avatar-8"
role="img"
>
<span
class="fui-Avatar__initials"
id="avatar-8__initials"
>
KA
</span>
</span>
<span
class="fui-Tag__icon"
>
i
</span>
<span
class="fui-Tag__primaryText"
>
Primary text
</span>
<span
class="fui-Tag__secondaryText"
>
Secondary text
</span>
</span>
<button
class="fui-Tag__dismissButton"
type="button"
>
<svg
aria-hidden="true"
class=""
fill="currentColor"
height="16"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="m2.4 2.55.07-.08a.75.75 0 0 1 .98-.07l.08.07L8 6.94l4.47-4.47a.75.75 0 1 1 1.06 1.06L9.06 8l4.47 4.47c.27.27.3.68.07.98l-.07.08a.75.75 0 0 1-.98.07l-.08-.07L8 9.06l-4.47 4.47a.75.75 0 0 1-1.06-1.06L6.94 8 2.47 3.53a.75.75 0 0 1-.07-.98l.07-.08-.07.08Z"
fill="currentColor"
/>
</svg>
</button>
</div>
</div>
`;
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,17 @@ export const renderTag_unstable = (state: TagState) => {
const { slots, slotProps } = getSlotsNext<TagSlots>(state);

// TODO Add additional slots in the appropriate place
return <slots.root {...slotProps.root} />;
return (
<slots.root {...slotProps.root}>
{slots.content && (
<slots.content {...slotProps.content}>
{slots.avatar && <slots.avatar {...slotProps.avatar} />}
{slots.icon && <slots.icon {...slotProps.icon} />}
{slots.primaryText && <slots.primaryText {...slotProps.primaryText} />}
{slots.secondaryText && <slots.secondaryText {...slotProps.secondaryText} />}
</slots.content>
)}
{slots.dismissButton && state.dismissable && <slots.dismissButton {...slotProps.dismissButton} />}
</slots.root>
);
};
28 changes: 0 additions & 28 deletions packages/react-components/react-tags/src/components/Tag/useTag.ts

This file was deleted.

60 changes: 60 additions & 0 deletions packages/react-components/react-tags/src/components/Tag/useTag.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import * as React from 'react';
import { getNativeElementProps, resolveShorthand } from '@fluentui/react-utilities';
import { Dismiss16Filled } from '@fluentui/react-icons';
import { Avatar } from '@fluentui/react-avatar';
import type { TagProps, TagState } from './Tag.types';

/**
* Create the state required to render Tag.
*
* The returned state can be modified with hooks such as useTagStyles_unstable,
* before being passed to renderTag_unstable.
*
* @param props - props from this instance of Tag
* @param ref - reference to root HTMLElement of Tag
*/
export const useTag_unstable = (props: TagProps, ref: React.Ref<HTMLElement>): TagState => {
const {
checked = false,
disabled = false,
dismissable = false,
shape = 'rounded',
size = 'medium',
appearance = 'filled-lighter',
} = props;

return {
components: {
root: 'div',
content: 'span',
avatar: Avatar,
icon: 'span',
primaryText: 'span',
secondaryText: 'span',
dismissButton: 'button',
},
checked,
disabled,
dismissable,
shape,
size,
appearance,
root: getNativeElementProps('div', {
ref,
...props,
}),
content: resolveShorthand(props.content, { required: true }),
avatar: resolveShorthand(props.avatar),
icon: resolveShorthand(props.icon),
primaryText: resolveShorthand(props.primaryText),
secondaryText: resolveShorthand(props.secondaryText),
dismissButton: resolveShorthand(props.dismissButton, {
required: true,
defaultProps: {
disabled,
type: 'button',
children: <Dismiss16Filled />,
},
}),
};
};
Loading

0 comments on commit a3edb5d

Please sign in to comment.