Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "feat: add swatch picker components",
"packageName": "@fluentui/react-headless-components-preview",
"email": "dmytrokirpa@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import * as Skeleton from '@fluentui/react-headless-components-preview/skeleton'
import * as Slider from '@fluentui/react-headless-components-preview/slider';
import * as SpinButton from '@fluentui/react-headless-components-preview/spin-button';
import * as Spinner from '@fluentui/react-headless-components-preview/spinner';
import * as SwatchPicker from '@fluentui/react-headless-components-preview/swatch-picker';
import * as Switch from '@fluentui/react-headless-components-preview/switch';
import * as TabList from '@fluentui/react-headless-components-preview/tab-list';
import * as Tag from '@fluentui/react-headless-components-preview/tag';
Expand Down Expand Up @@ -85,6 +86,7 @@ console.log({
Slider,
SpinButton,
Spinner,
SwatchPicker,
Switch,
TabList,
Tag,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
## API Report File for "@fluentui/react-headless-components-preview"

> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).

```ts

import type { ColorSwatchBaseState } from '@fluentui/react-swatch-picker';
import { ColorSwatchBaseProps as ColorSwatchProps } from '@fluentui/react-swatch-picker';
import { ColorSwatchSlots } from '@fluentui/react-swatch-picker';
import type { EmptySwatchBaseState } from '@fluentui/react-swatch-picker';
import { EmptySwatchBaseProps as EmptySwatchProps } from '@fluentui/react-swatch-picker';
import { EmptySwatchSlots } from '@fluentui/react-swatch-picker';
import type { ForwardRefComponent } from '@fluentui/react-utilities';
import type { ImageSwatchBaseState } from '@fluentui/react-swatch-picker';
import { ImageSwatchBaseProps as ImageSwatchProps } from '@fluentui/react-swatch-picker';
import { ImageSwatchSlots } from '@fluentui/react-swatch-picker';
import type * as React_2 from 'react';
import { renderColorSwatch_unstable as renderColorSwatch } from '@fluentui/react-swatch-picker';
import { renderEmptySwatch_unstable as renderEmptySwatch } from '@fluentui/react-swatch-picker';
import { renderImageSwatch_unstable as renderImageSwatch } from '@fluentui/react-swatch-picker';
import { renderSwatchPicker_unstable as renderSwatchPicker } from '@fluentui/react-swatch-picker';
import { renderSwatchPickerRow_unstable as renderSwatchPickerRow } from '@fluentui/react-swatch-picker';
import type { SwatchPickerBaseState } from '@fluentui/react-swatch-picker';
import { SwatchPickerBaseProps as SwatchPickerProps } from '@fluentui/react-swatch-picker';
import type { SwatchPickerRowBaseState } from '@fluentui/react-swatch-picker';
import { SwatchPickerRowBaseProps as SwatchPickerRowProps } from '@fluentui/react-swatch-picker';
import { SwatchPickerRowSlots } from '@fluentui/react-swatch-picker';
import { SwatchPickerSlots } from '@fluentui/react-swatch-picker';
import { useSwatchPickerContextValue_unstable as useSwatchPickerContextValue } from '@fluentui/react-swatch-picker';
import { useSwatchPickerContextValues } from '@fluentui/react-swatch-picker';

// @public (undocumented)
export const ColorSwatch: ForwardRefComponent<ColorSwatchProps>;

export { ColorSwatchProps }

export { ColorSwatchSlots }

// @public (undocumented)
export type ColorSwatchState = ColorSwatchBaseState & {
root: {
'data-selected'?: string;
'data-disabled'?: string;
};
};

// @public (undocumented)
export const EmptySwatch: ForwardRefComponent<EmptySwatchProps>;

export { EmptySwatchProps }

export { EmptySwatchSlots }

// @public (undocumented)
export type EmptySwatchState = EmptySwatchBaseState & {
root: {
'data-selected'?: string;
'data-disabled'?: string;
};
};

// @public (undocumented)
export const ImageSwatch: ForwardRefComponent<ImageSwatchProps>;

export { ImageSwatchProps }

export { ImageSwatchSlots }

// @public (undocumented)
export type ImageSwatchState = ImageSwatchBaseState & {
root: {
'data-selected'?: string;
};
};

export { renderColorSwatch }

export { renderEmptySwatch }

export { renderImageSwatch }

export { renderSwatchPicker }

export { renderSwatchPickerRow }

// @public
export const SwatchPicker: ForwardRefComponent<SwatchPickerProps>;

export { SwatchPickerProps }

// @public (undocumented)
export const SwatchPickerRow: ForwardRefComponent<SwatchPickerRowProps>;

export { SwatchPickerRowProps }

export { SwatchPickerRowSlots }

// @public (undocumented)
export type SwatchPickerRowState = SwatchPickerRowBaseState & {
root: SwatchPickerRowBaseState['root'] & {
focusgrouprow?: string;
};
};

export { SwatchPickerSlots }

// @public (undocumented)
export type SwatchPickerState = SwatchPickerBaseState & {
root: {
'data-layout'?: SwatchPickerBaseState['layout'];
focusgroup?: string;
};
};

// @public (undocumented)
export const useColorSwatch: (props: ColorSwatchProps, ref: React_2.Ref<HTMLButtonElement>) => ColorSwatchState;

// @public (undocumented)
export const useEmptySwatch: (props: EmptySwatchProps, ref: React_2.Ref<HTMLButtonElement>) => EmptySwatchState;

// @public (undocumented)
export const useImageSwatch: (props: ImageSwatchProps, ref: React_2.Ref<HTMLButtonElement>) => ImageSwatchState;

// @public (undocumented)
export const useSwatchPicker: (props: SwatchPickerProps, ref: React_2.Ref<HTMLDivElement>) => SwatchPickerState;

export { useSwatchPickerContextValue }

export { useSwatchPickerContextValues }

// @public (undocumented)
export const useSwatchPickerRow: (props: SwatchPickerRowProps, ref: React_2.Ref<HTMLDivElement>) => SwatchPickerRowState;

// (No @packageDocumentation comment for this package)

```
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
"@fluentui/react-slider": "^9.6.4",
"@fluentui/react-spinbutton": "^9.6.4",
"@fluentui/react-spinner": "^9.8.4",
"@fluentui/react-swatch-picker": "^9.5.4",
"@fluentui/react-switch": "^9.7.4",
"@fluentui/react-tabs": "^9.12.3",
"@fluentui/react-tabster": "^9.26.16",
Expand Down Expand Up @@ -310,6 +311,12 @@
"import": "./lib/spinner.js",
"require": "./lib-commonjs/spinner.js"
},
"./swatch-picker": {
"types": "./dist/swatch-picker.d.ts",
"node": "./lib-commonjs/swatch-picker.js",
"import": "./lib/swatch-picker.js",
"require": "./lib-commonjs/swatch-picker.js"
},
"./switch": {
"types": "./dist/switch.d.ts",
"node": "./lib-commonjs/switch.js",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import * as React from 'react';
import { render } from '@testing-library/react';
import { isConformant } from '../../../testing/isConformant';
import { ColorSwatch } from './ColorSwatch';
import { SwatchPicker } from '../SwatchPicker';

describe('ColorSwatch', () => {
isConformant({
Component: ColorSwatch,
displayName: 'ColorSwatch',
disabledTests: ['has-top-level-file-extra'],
});

it('emits selected and disabled state attributes', () => {
const { getByRole } = render(<ColorSwatch color="#f09" value="pink" aria-label="Pink" disabled />, {
wrapper: ({ children }) => (
<SwatchPicker aria-label="Colors" selectedValue="pink">
{children}
</SwatchPicker>
),
});
const swatch = getByRole('radio');
expect(swatch).toHaveAttribute('aria-label', 'Pink');
expect(swatch).toHaveAttribute('aria-checked', 'true');
expect(swatch).toHaveAttribute('data-selected');
expect(swatch).toHaveAttribute('data-disabled');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
'use client';

import * as React from 'react';
import type { ForwardRefComponent } from '@fluentui/react-utilities';
import type { ColorSwatchProps } from './ColorSwatch.types';
import { useColorSwatch } from './useColorSwatch';
import { renderColorSwatch } from './renderColorSwatch';

export const ColorSwatch: ForwardRefComponent<ColorSwatchProps> = React.forwardRef((props, ref) => {
const state = useColorSwatch(props, ref);
return renderColorSwatch(state);
});

ColorSwatch.displayName = 'ColorSwatch';
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type { ColorSwatchBaseState } from '@fluentui/react-swatch-picker';

export type { ColorSwatchBaseProps as ColorSwatchProps, ColorSwatchSlots } from '@fluentui/react-swatch-picker';

export type ColorSwatchState = ColorSwatchBaseState & {
root: {
/**
* Whether ColorSwatch is selected
*/
'data-selected'?: string;
/**
* Whether ColorSwatch is disabled
*/
'data-disabled'?: string;
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export { ColorSwatch } from './ColorSwatch';
export { renderColorSwatch } from './renderColorSwatch';
export { useColorSwatch } from './useColorSwatch';
export type { ColorSwatchSlots, ColorSwatchProps, ColorSwatchState } from './ColorSwatch.types';
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { renderColorSwatch_unstable as renderColorSwatch } from '@fluentui/react-swatch-picker';
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
'use client';

import type * as React from 'react';
import { useColorSwatchBase_unstable } from '@fluentui/react-swatch-picker';
import { stringifyDataAttribute } from '../../../utils';
import type { ColorSwatchProps, ColorSwatchState } from './ColorSwatch.types';

export const useColorSwatch = (props: ColorSwatchProps, ref: React.Ref<HTMLButtonElement>): ColorSwatchState => {
const state: ColorSwatchState = useColorSwatchBase_unstable(props, ref);

// eslint-disable-next-line react-hooks/immutability
state.root['data-selected'] = stringifyDataAttribute(state.selected);
// eslint-disable-next-line react-hooks/immutability
state.root['data-disabled'] = stringifyDataAttribute(state.disabled);

return state;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import * as React from 'react';
import { render } from '@testing-library/react';
import { isConformant } from '../../../testing/isConformant';
import { EmptySwatch } from './EmptySwatch';

describe('EmptySwatch', () => {
isConformant({ Component: EmptySwatch, displayName: 'EmptySwatch', disabledTests: ['has-top-level-file-extra'] });

it('renders a native radio swatch with state attributes', () => {
const swatch = render(<EmptySwatch aria-label="Empty" disabled />).getByRole('radio');

expect(swatch).toHaveAttribute('aria-checked', 'false');
expect(swatch).not.toHaveAttribute('data-selected');
expect(swatch).toHaveAttribute('data-disabled');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
'use client';

import * as React from 'react';
import type { ForwardRefComponent } from '@fluentui/react-utilities';
import type { EmptySwatchProps } from './EmptySwatch.types';
import { useEmptySwatch } from './useEmptySwatch';
import { renderEmptySwatch } from './renderEmptySwatch';

export const EmptySwatch: ForwardRefComponent<EmptySwatchProps> = React.forwardRef((props, ref) => {
const state = useEmptySwatch(props, ref);
return renderEmptySwatch(state);
});

EmptySwatch.displayName = 'EmptySwatch';
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type { EmptySwatchBaseState } from '@fluentui/react-swatch-picker';

export type { EmptySwatchBaseProps as EmptySwatchProps, EmptySwatchSlots } from '@fluentui/react-swatch-picker';

export type EmptySwatchState = EmptySwatchBaseState & {
root: {
/**
* Whether EmptySwatch is selected
*/
'data-selected'?: string;
/**
* Whether EmptySwatch is disabled
*/
'data-disabled'?: string;
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export { EmptySwatch } from './EmptySwatch';
export { renderEmptySwatch } from './renderEmptySwatch';
export { useEmptySwatch } from './useEmptySwatch';
export type { EmptySwatchSlots, EmptySwatchProps, EmptySwatchState } from './EmptySwatch.types';
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { renderEmptySwatch_unstable as renderEmptySwatch } from '@fluentui/react-swatch-picker';
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
'use client';

import type * as React from 'react';
import { useEmptySwatchBase_unstable } from '@fluentui/react-swatch-picker';
import { stringifyDataAttribute } from '../../../utils';
import type { EmptySwatchProps, EmptySwatchState } from './EmptySwatch.types';

export const useEmptySwatch = (props: EmptySwatchProps, ref: React.Ref<HTMLButtonElement>): EmptySwatchState => {
const state: EmptySwatchState = useEmptySwatchBase_unstable(props, ref);

// eslint-disable-next-line react-hooks/immutability
state.root['data-selected'] = stringifyDataAttribute(state.root['aria-checked']);
// eslint-disable-next-line react-hooks/immutability
state.root['data-disabled'] = stringifyDataAttribute(state.root.disabled);

return state;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import * as React from 'react';
import { render } from '@testing-library/react';
import { isConformant } from '../../../testing/isConformant';
import { ImageSwatch } from './ImageSwatch';
import { SwatchPicker } from '../SwatchPicker';

describe('ImageSwatch', () => {
isConformant({ Component: ImageSwatch, displayName: 'ImageSwatch', disabledTests: ['has-top-level-file-extra'] });

it('renders an image swatch with its selection state', () => {
const { getByRole } = render(<ImageSwatch src="image.png" value="image" aria-label="Image" />, {
wrapper: ({ children }) => <SwatchPicker selectedValue="image">{children}</SwatchPicker>,
});

expect(getByRole('radio')).toHaveAttribute('data-selected');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
'use client';

import * as React from 'react';
import type { ForwardRefComponent } from '@fluentui/react-utilities';
import type { ImageSwatchProps } from './ImageSwatch.types';
import { useImageSwatch } from './useImageSwatch';
import { renderImageSwatch } from './renderImageSwatch';

export const ImageSwatch: ForwardRefComponent<ImageSwatchProps> = React.forwardRef((props, ref) => {
const state = useImageSwatch(props, ref);
return renderImageSwatch(state);
});

ImageSwatch.displayName = 'ImageSwatch';
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { ImageSwatchBaseState } from '@fluentui/react-swatch-picker';

export type { ImageSwatchSlots, ImageSwatchBaseProps as ImageSwatchProps } from '@fluentui/react-swatch-picker';

export type ImageSwatchState = ImageSwatchBaseState & {
root: {
/**
* Whether ImageSwatch is selected
*/
'data-selected'?: string;
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export { ImageSwatch } from './ImageSwatch';
export { renderImageSwatch } from './renderImageSwatch';
export { useImageSwatch } from './useImageSwatch';
export type { ImageSwatchSlots, ImageSwatchProps, ImageSwatchState } from './ImageSwatch.types';
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { renderImageSwatch_unstable as renderImageSwatch } from '@fluentui/react-swatch-picker';
Loading
Loading