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": "minor",
"comment": "feat: add motion to Checkbox",
"packageName": "@fluentui/react-checkbox",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export type CheckboxSlots = {
label?: Slot<typeof Label>;
input: NonNullable<Slot<'input'>>;
indicator: Slot<'div', 'span'>;
checkmarkIcon: Slot<'svg', 'div' | 'span' | 'img'>;
};

// @public
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,13 @@ describe('Checkbox', () => {
expect(renderedComponent.container).toMatchSnapshot();
});

it('renders a custom checkmark icon', () => {
const renderedComponent = render(
<Checkbox checkmarkIcon={<span id="checkmark-icon" />} label="Custom checkmark icon" />,
);
expect(renderedComponent.container).toMatchSnapshot();
});

it('respects id prop', () => {
const renderedComponent = render(<Checkbox id="checkbox" label="Checkbox" />);
expect(renderedComponent.getByRole('checkbox').id).toEqual('checkbox');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ export type CheckboxSlots = {
// but changing the signature would be a breaking change
// TODO: change the default value to 'span' in the next major
indicator: Slot<'div', 'span'>;

/**
* The checkmark icon displayed inside the indicator when checked.
*/
checkmarkIcon: Slot<'svg', 'span' | 'img'>;
};

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,44 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Checkbox renders a custom checkmark icon 1`] = `
<div>
<span
class="fui-Checkbox"
>
<input
class="fui-Checkbox__input"
id="checkbox-rd"
type="checkbox"
/>
<div
aria-hidden="true"
class="fui-Checkbox__indicator"
>
<svg
aria-hidden="true"
class="fui-Checkbox__checkmarkIcon"
fill="currentColor"
height="12"
viewBox="0 0 12 12"
width="12"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M9.76 3.2c.3.29.32.76.04 1.06l-4.25 4.5a.75.75 0 0 1-1.08.02L2.22 6.53a.75.75 0 0 1 1.06-1.06l1.7 1.7L8.7 3.24a.75.75 0 0 1 1.06-.04Z"
fill="currentColor"
/>
</svg>
</div>
<label
class="fui-Label fui-Checkbox__label"
for="checkbox-rd"
>
Custom checkmark icon
</label>
</span>
</div>
`;

exports[`Checkbox renders a custom indicator 1`] = `
<div>
<span
Expand All @@ -14,9 +53,20 @@ exports[`Checkbox renders a custom indicator 1`] = `
aria-hidden="true"
class="fui-Checkbox__indicator"
>
<span
id="indicator"
/>
<svg
aria-hidden="true"
class="fui-Checkbox__checkmarkIcon"
fill="currentColor"
height="12"
viewBox="0 0 12 12"
width="12"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M9.76 3.2c.3.29.32.76.04 1.06l-4.25 4.5a.75.75 0 0 1-1.08.02L2.22 6.53a.75.75 0 0 1 1.06-1.06l1.7 1.7L8.7 3.24a.75.75 0 0 1 1.06-.04Z"
fill="currentColor"
/>
</svg>
</div>
<label
class="fui-Label fui-Checkbox__label"
Expand All @@ -41,7 +91,22 @@ exports[`Checkbox renders a default state 1`] = `
<div
aria-hidden="true"
class="fui-Checkbox__indicator"
/>
>
<svg
aria-hidden="true"
class="fui-Checkbox__checkmarkIcon"
fill="currentColor"
height="12"
viewBox="0 0 12 12"
width="12"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M9.76 3.2c.3.29.32.76.04 1.06l-4.25 4.5a.75.75 0 0 1-1.08.02L2.22 6.53a.75.75 0 0 1 1.06-1.06l1.7 1.7L8.7 3.24a.75.75 0 0 1 1.06-.04Z"
fill="currentColor"
/>
</svg>
</div>
</span>
</div>
`;
Expand All @@ -63,7 +128,7 @@ exports[`Checkbox renders checked 1`] = `
>
<svg
aria-hidden="true"
class=""
class="fui-Checkbox__checkmarkIcon"
fill="currentColor"
height="12"
viewBox="0 0 12 12"
Expand Down Expand Up @@ -100,20 +165,9 @@ exports[`Checkbox renders mixed 1`] = `
aria-hidden="true"
class="fui-Checkbox__indicator"
>
<svg
aria-hidden="true"
class=""
fill="currentColor"
height="12"
viewBox="0 0 12 12"
width="12"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M2 4c0-1.1.9-2 2-2h4a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V4Z"
fill="currentColor"
/>
</svg>
<span
class="fui-Checkbox__checkmarkIcon"
/>
</div>
<label
class="fui-Label fui-Checkbox__label"
Expand All @@ -138,7 +192,22 @@ exports[`Checkbox renders with a label 1`] = `
<div
aria-hidden="true"
class="fui-Checkbox__indicator"
/>
>
<svg
aria-hidden="true"
class="fui-Checkbox__checkmarkIcon"
fill="currentColor"
height="12"
viewBox="0 0 12 12"
width="12"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M9.76 3.2c.3.29.32.76.04 1.06l-4.25 4.5a.75.75 0 0 1-1.08.02L2.22 6.53a.75.75 0 0 1 1.06-1.06l1.7 1.7L8.7 3.24a.75.75 0 0 1 1.06-.04Z"
fill="currentColor"
/>
</svg>
</div>
<label
class="fui-Label fui-Checkbox__label"
for="checkbox-r9"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ export const renderCheckbox_unstable = (state: CheckboxState): JSXElement => {
<state.root>
<state.input />
{state.labelPosition === 'before' && state.label && <state.label />}
{state.indicator && <state.indicator />}
{state.indicator && state.checkmarkIcon && (
<state.indicator>
<state.checkmarkIcon />
</state.indicator>
)}
{state.labelPosition === 'after' && state.label && <state.label />}
</state.root>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,7 @@ import {
slot,
} from '@fluentui/react-utilities';
import { CheckboxProps, CheckboxState } from './Checkbox.types';
import {
Checkmark12Filled,
Checkmark16Filled,
Square12Filled,
Square16Filled,
CircleFilled,
} from '@fluentui/react-icons';
import { Checkmark12Filled, Checkmark16Filled } from '@fluentui/react-icons';
import { Label } from '@fluentui/react-label';
import { useFocusWithin } from '@fluentui/react-tabster';

Expand Down Expand Up @@ -54,15 +48,10 @@ export const useCheckbox_unstable = (props: CheckboxProps, ref: React.Ref<HTMLIn
const mixed = checked === 'mixed';
const id = useId('checkbox-', nativeProps.primary.id);

let checkmarkIcon;
if (mixed) {
if (shape === 'circular') {
checkmarkIcon = <CircleFilled />;
} else {
checkmarkIcon = size === 'large' ? <Square16Filled /> : <Square12Filled />;
}
} else if (checked) {
checkmarkIcon = size === 'large' ? <Checkmark16Filled /> : <Checkmark12Filled />;
let checkmarkIcon: React.ElementType = 'span';

if (!mixed) {
checkmarkIcon = size === 'large' ? Checkmark16Filled : Checkmark12Filled;
}

const state: CheckboxState = {
Expand All @@ -75,6 +64,7 @@ export const useCheckbox_unstable = (props: CheckboxProps, ref: React.Ref<HTMLIn
root: 'span',
input: 'input',
indicator: 'div',
checkmarkIcon,
label: Label,
},
root: slot.always(props.root, {
Expand Down Expand Up @@ -107,10 +97,13 @@ export const useCheckbox_unstable = (props: CheckboxProps, ref: React.Ref<HTMLIn
renderByDefault: true,
defaultProps: {
'aria-hidden': true,
children: checkmarkIcon,
},
elementType: 'div',
}),
checkmarkIcon: slot.optional(props.checkmarkIcon, {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I maybe missing something, but what's the main idea behind having this as a separate slot?

renderByDefault: true,
elementType: 'svg',
}),
};

state.input.onChange = useEventCallback(ev => {
Expand Down
Loading
Loading