Browse Source

feat: add new component `Segmented` (#34319)

* feat: basic implements in antd

* feat: use rc-segmented

* feat: add some demos

* feat: support dark theme

* docs: add more demos

* feat: support `icon` in segmented

* docs: format content

* test: add test cases

* docs: update docs

* chore: update

* fix: lint issues

* style: add border-radius for selected effect

* style: change border-radius-base

* chore: update gitignore

* feat: segmented support block

* chore: update gitignore

* feat: support block

* feat: add test case

* feat: support size

* docs: update demos

* chore: update

* chore: update

* test: update

* style: update colors

* style: hover and focus styles

* doc: add version mark in docs

* fix: style lint issue

* fix: some lint issues

* doc: add version in demo mds

* doc: add version mark before api details

* Revert "doc: add version in demo mds"

This reverts commit 2a9e86f6ffc2331f7d73eae6edac7c889358a838.

* test: only test for thumb appear

* style: update color vars

* style: add transition

* style: text vertial align

* test: update snapshots

* feat: add preview svg and support dark theme

* fix: update test snapshot

* docs: update docs

* fix: remove useless z-index

* build: add 1kb for bundlesize of antd.variable.min.css

Co-authored-by: taian.lta <taian.lta@antgroup.com>
pull/34972/head
vagusX 3 years ago
committed by GitHub
parent
commit
ae4e660a05
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      .gitignore
  2. 1
      components/__tests__/__snapshots__/index.test.js.snap
  3. 3
      components/index.tsx
  4. 861
      components/segmented/__tests__/__snapshots__/demo-extend.test.ts.snap
  5. 861
      components/segmented/__tests__/__snapshots__/demo.test.js.snap
  6. 638
      components/segmented/__tests__/__snapshots__/index.test.tsx.snap
  7. 3
      components/segmented/__tests__/demo-extend.test.ts
  8. 3
      components/segmented/__tests__/demo.test.js
  9. 5
      components/segmented/__tests__/image.test.ts
  10. 360
      components/segmented/__tests__/index.test.tsx
  11. 33
      components/segmented/demo/basic.md
  12. 23
      components/segmented/demo/block.md
  13. 33
      components/segmented/demo/controlled.md
  14. 97
      components/segmented/demo/custom.md
  15. 35
      components/segmented/demo/disabled.md
  16. 43
      components/segmented/demo/dynamic.md
  17. 29
      components/segmented/demo/size.md
  18. 37
      components/segmented/demo/with-icon.md
  19. 27
      components/segmented/index.en-US.md
  20. 87
      components/segmented/index.tsx
  21. 30
      components/segmented/index.zh-CN.md
  22. 116
      components/segmented/style/index.less
  23. 2
      components/segmented/style/index.tsx
  24. 24
      components/segmented/style/mixins.less
  25. 8
      components/style/themes/dark.less
  26. 8
      components/style/themes/default.less
  27. 8
      components/style/themes/variable.less
  28. 3
      package.json
  29. 1
      tests/__snapshots__/index.test.js.snap

2
.gitignore

@ -67,3 +67,5 @@ __image_snapshots__/
/jest-stare
/imageSnapshots
/imageDiffSnapshots
.devcontainer*

1
components/__tests__/__snapshots__/index.test.js.snap

@ -45,6 +45,7 @@ Array [
"Rate",
"Result",
"Row",
"Segmented",
"Select",
"Skeleton",
"Slider",

3
components/index.tsx

@ -154,6 +154,9 @@ export { default as Row } from './row';
export type { SelectProps } from './select';
export { default as Select } from './select';
export type { SegmentedProps } from './segmented';
export { default as Segmented } from './segmented';
export type { SkeletonProps } from './skeleton';
export { default as Skeleton } from './skeleton';

861
components/segmented/__tests__/__snapshots__/demo-extend.test.ts.snap

@ -0,0 +1,861 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders ./components/segmented/demo/basic.md extend context correctly 1`] = `
<div
class="ant-segmented"
>
<label
class="ant-segmented-item ant-segmented-item-selected"
>
<input
checked=""
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Daily"
>
Daily
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Weekly"
>
Weekly
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Monthly"
>
Monthly
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Quarterly"
>
Quarterly
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Yearly"
>
Yearly
</div>
</label>
</div>
`;
exports[`renders ./components/segmented/demo/block.md extend context correctly 1`] = `
<div
class="ant-segmented ant-segmented-block"
>
<label
class="ant-segmented-item ant-segmented-item-selected"
>
<input
checked=""
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="123"
>
123
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="456"
>
456
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="longtext-longtext-longtext-longtext"
>
longtext-longtext-longtext-longtext
</div>
</label>
</div>
`;
exports[`renders ./components/segmented/demo/controlled.md extend context correctly 1`] = `
<div
class="ant-segmented"
>
<label
class="ant-segmented-item ant-segmented-item-selected"
>
<input
checked=""
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Map"
>
Map
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Transit"
>
Transit
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Satellite"
>
Satellite
</div>
</label>
</div>
`;
exports[`renders ./components/segmented/demo/custom.md extend context correctly 1`] = `
Array [
<div
class="ant-segmented"
>
<label
class="ant-segmented-item ant-segmented-item-selected"
>
<input
checked=""
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
>
<span
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
/>
</span>
<div>
User 1
</div>
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
>
<span
class="ant-avatar ant-avatar-circle"
style="background-color:#f56a00"
>
<span
class="ant-avatar-string"
style="opacity:0"
>
K
</span>
</span>
<div>
User 2
</div>
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
>
<span
class="ant-avatar ant-avatar-circle ant-avatar-icon"
style="background-color:#87d068"
>
<span
aria-label="user"
class="anticon anticon-user"
role="img"
>
<svg
aria-hidden="true"
data-icon="user"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M858.5 763.6a374 374 0 00-80.6-119.5 375.63 375.63 0 00-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 00-80.6 119.5A371.7 371.7 0 00136 901.8a8 8 0 008 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 008-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"
/>
</svg>
</span>
</span>
<div>
User 3
</div>
</div>
</label>
</div>,
<br />,
<div
class="ant-segmented"
>
<label
class="ant-segmented-item ant-segmented-item-selected"
>
<input
checked=""
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
>
<div>
Spring
</div>
<div>
Jan-Mar
</div>
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
>
<div>
Summer
</div>
<div>
Apr-Jun
</div>
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
>
<div>
Autumn
</div>
<div>
Jul-Sept
</div>
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
>
<div>
Winter
</div>
<div>
Oct-Dec
</div>
</div>
</label>
</div>,
]
`;
exports[`renders ./components/segmented/demo/disabled.md extend context correctly 1`] = `
Array [
<div
class="ant-segmented ant-segmented-disabled"
>
<label
class="ant-segmented-item ant-segmented-item-selected"
>
<input
checked=""
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Map"
>
Map
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Transit"
>
Transit
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Satellite"
>
Satellite
</div>
</label>
</div>,
<br />,
<div
class="ant-segmented"
>
<label
class="ant-segmented-item ant-segmented-item-selected"
>
<input
checked=""
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Daily"
>
Daily
</div>
</label>
<label
class="ant-segmented-item ant-segmented-item-disabled"
>
<input
class="ant-segmented-item-input"
disabled=""
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Weekly"
>
Weekly
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Monthly"
>
Monthly
</div>
</label>
<label
class="ant-segmented-item ant-segmented-item-disabled"
>
<input
class="ant-segmented-item-input"
disabled=""
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Quarterly"
>
Quarterly
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Yearly"
>
Yearly
</div>
</label>
</div>,
]
`;
exports[`renders ./components/segmented/demo/dynamic.md extend context correctly 1`] = `
Array [
<div
class="ant-segmented"
>
<label
class="ant-segmented-item ant-segmented-item-selected"
>
<input
checked=""
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Daily"
>
Daily
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Weekly"
>
Weekly
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Monthly"
>
Monthly
</div>
</label>
</div>,
<br />,
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Load more options
</span>
</button>,
]
`;
exports[`renders ./components/segmented/demo/size.md extend context correctly 1`] = `
Array [
<div
class="ant-segmented ant-segmented-lg"
>
<label
class="ant-segmented-item ant-segmented-item-selected"
>
<input
checked=""
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Daily"
>
Daily
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Weekly"
>
Weekly
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Monthly"
>
Monthly
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Quarterly"
>
Quarterly
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Yearly"
>
Yearly
</div>
</label>
</div>,
<br />,
<div
class="ant-segmented"
>
<label
class="ant-segmented-item ant-segmented-item-selected"
>
<input
checked=""
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Daily"
>
Daily
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Weekly"
>
Weekly
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Monthly"
>
Monthly
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Quarterly"
>
Quarterly
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Yearly"
>
Yearly
</div>
</label>
</div>,
<br />,
<div
class="ant-segmented ant-segmented-sm"
>
<label
class="ant-segmented-item ant-segmented-item-selected"
>
<input
checked=""
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Daily"
>
Daily
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Weekly"
>
Weekly
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Monthly"
>
Monthly
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Quarterly"
>
Quarterly
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Yearly"
>
Yearly
</div>
</label>
</div>,
]
`;
exports[`renders ./components/segmented/demo/with-icon.md extend context correctly 1`] = `
<div
class="ant-segmented"
>
<label
class="ant-segmented-item ant-segmented-item-selected"
>
<input
checked=""
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
>
<span
class="ant-segmented-item-icon"
>
<span
aria-label="bars"
class="anticon anticon-bars"
role="img"
>
<svg
aria-hidden="true"
data-icon="bars"
fill="currentColor"
focusable="false"
height="1em"
viewBox="0 0 1024 1024"
width="1em"
>
<path
d="M912 192H328c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h584c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 284H328c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h584c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 284H328c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h584c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM104 228a56 56 0 10112 0 56 56 0 10-112 0zm0 284a56 56 0 10112 0 56 56 0 10-112 0zm0 284a56 56 0 10112 0 56 56 0 10-112 0z"
/>
</svg>
</span>
</span>
<span>
List
</span>
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
>
<span
class="ant-segmented-item-icon"
>
<span
aria-label="appstore"
class="anticon anticon-appstore"
role="img"
>
<svg
aria-hidden="true"
data-icon="appstore"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M464 144H160c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V160c0-8.8-7.2-16-16-16zm-52 268H212V212h200v200zm452-268H560c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V160c0-8.8-7.2-16-16-16zm-52 268H612V212h200v200zM464 544H160c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V560c0-8.8-7.2-16-16-16zm-52 268H212V612h200v200zm452-268H560c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V560c0-8.8-7.2-16-16-16zm-52 268H612V612h200v200z"
/>
</svg>
</span>
</span>
<span>
Kanban
</span>
</div>
</label>
</div>
`;

861
components/segmented/__tests__/__snapshots__/demo.test.js.snap

@ -0,0 +1,861 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders ./components/segmented/demo/basic.md correctly 1`] = `
<div
class="ant-segmented"
>
<label
class="ant-segmented-item ant-segmented-item-selected"
>
<input
checked=""
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Daily"
>
Daily
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Weekly"
>
Weekly
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Monthly"
>
Monthly
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Quarterly"
>
Quarterly
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Yearly"
>
Yearly
</div>
</label>
</div>
`;
exports[`renders ./components/segmented/demo/block.md correctly 1`] = `
<div
class="ant-segmented ant-segmented-block"
>
<label
class="ant-segmented-item ant-segmented-item-selected"
>
<input
checked=""
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="123"
>
123
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="456"
>
456
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="longtext-longtext-longtext-longtext"
>
longtext-longtext-longtext-longtext
</div>
</label>
</div>
`;
exports[`renders ./components/segmented/demo/controlled.md correctly 1`] = `
<div
class="ant-segmented"
>
<label
class="ant-segmented-item ant-segmented-item-selected"
>
<input
checked=""
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Map"
>
Map
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Transit"
>
Transit
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Satellite"
>
Satellite
</div>
</label>
</div>
`;
exports[`renders ./components/segmented/demo/custom.md correctly 1`] = `
Array [
<div
class="ant-segmented"
>
<label
class="ant-segmented-item ant-segmented-item-selected"
>
<input
checked=""
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
>
<span
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
/>
</span>
<div>
User 1
</div>
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
>
<span
class="ant-avatar ant-avatar-circle"
style="background-color:#f56a00"
>
<span
class="ant-avatar-string"
style="opacity:0"
>
K
</span>
</span>
<div>
User 2
</div>
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
>
<span
class="ant-avatar ant-avatar-circle ant-avatar-icon"
style="background-color:#87d068"
>
<span
aria-label="user"
class="anticon anticon-user"
role="img"
>
<svg
aria-hidden="true"
data-icon="user"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M858.5 763.6a374 374 0 00-80.6-119.5 375.63 375.63 0 00-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 00-80.6 119.5A371.7 371.7 0 00136 901.8a8 8 0 008 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 008-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"
/>
</svg>
</span>
</span>
<div>
User 3
</div>
</div>
</label>
</div>,
<br />,
<div
class="ant-segmented"
>
<label
class="ant-segmented-item ant-segmented-item-selected"
>
<input
checked=""
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
>
<div>
Spring
</div>
<div>
Jan-Mar
</div>
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
>
<div>
Summer
</div>
<div>
Apr-Jun
</div>
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
>
<div>
Autumn
</div>
<div>
Jul-Sept
</div>
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
>
<div>
Winter
</div>
<div>
Oct-Dec
</div>
</div>
</label>
</div>,
]
`;
exports[`renders ./components/segmented/demo/disabled.md correctly 1`] = `
Array [
<div
class="ant-segmented ant-segmented-disabled"
>
<label
class="ant-segmented-item ant-segmented-item-selected"
>
<input
checked=""
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Map"
>
Map
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Transit"
>
Transit
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Satellite"
>
Satellite
</div>
</label>
</div>,
<br />,
<div
class="ant-segmented"
>
<label
class="ant-segmented-item ant-segmented-item-selected"
>
<input
checked=""
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Daily"
>
Daily
</div>
</label>
<label
class="ant-segmented-item ant-segmented-item-disabled"
>
<input
class="ant-segmented-item-input"
disabled=""
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Weekly"
>
Weekly
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Monthly"
>
Monthly
</div>
</label>
<label
class="ant-segmented-item ant-segmented-item-disabled"
>
<input
class="ant-segmented-item-input"
disabled=""
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Quarterly"
>
Quarterly
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Yearly"
>
Yearly
</div>
</label>
</div>,
]
`;
exports[`renders ./components/segmented/demo/dynamic.md correctly 1`] = `
Array [
<div
class="ant-segmented"
>
<label
class="ant-segmented-item ant-segmented-item-selected"
>
<input
checked=""
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Daily"
>
Daily
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Weekly"
>
Weekly
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Monthly"
>
Monthly
</div>
</label>
</div>,
<br />,
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Load more options
</span>
</button>,
]
`;
exports[`renders ./components/segmented/demo/size.md correctly 1`] = `
Array [
<div
class="ant-segmented ant-segmented-lg"
>
<label
class="ant-segmented-item ant-segmented-item-selected"
>
<input
checked=""
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Daily"
>
Daily
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Weekly"
>
Weekly
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Monthly"
>
Monthly
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Quarterly"
>
Quarterly
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Yearly"
>
Yearly
</div>
</label>
</div>,
<br />,
<div
class="ant-segmented"
>
<label
class="ant-segmented-item ant-segmented-item-selected"
>
<input
checked=""
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Daily"
>
Daily
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Weekly"
>
Weekly
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Monthly"
>
Monthly
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Quarterly"
>
Quarterly
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Yearly"
>
Yearly
</div>
</label>
</div>,
<br />,
<div
class="ant-segmented ant-segmented-sm"
>
<label
class="ant-segmented-item ant-segmented-item-selected"
>
<input
checked=""
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Daily"
>
Daily
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Weekly"
>
Weekly
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Monthly"
>
Monthly
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Quarterly"
>
Quarterly
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Yearly"
>
Yearly
</div>
</label>
</div>,
]
`;
exports[`renders ./components/segmented/demo/with-icon.md correctly 1`] = `
<div
class="ant-segmented"
>
<label
class="ant-segmented-item ant-segmented-item-selected"
>
<input
checked=""
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
>
<span
class="ant-segmented-item-icon"
>
<span
aria-label="bars"
class="anticon anticon-bars"
role="img"
>
<svg
aria-hidden="true"
data-icon="bars"
fill="currentColor"
focusable="false"
height="1em"
viewBox="0 0 1024 1024"
width="1em"
>
<path
d="M912 192H328c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h584c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 284H328c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h584c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 284H328c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h584c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM104 228a56 56 0 10112 0 56 56 0 10-112 0zm0 284a56 56 0 10112 0 56 56 0 10-112 0zm0 284a56 56 0 10112 0 56 56 0 10-112 0z"
/>
</svg>
</span>
</span>
<span>
List
</span>
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
>
<span
class="ant-segmented-item-icon"
>
<span
aria-label="appstore"
class="anticon anticon-appstore"
role="img"
>
<svg
aria-hidden="true"
data-icon="appstore"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M464 144H160c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V160c0-8.8-7.2-16-16-16zm-52 268H212V212h200v200zm452-268H560c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V160c0-8.8-7.2-16-16-16zm-52 268H612V212h200v200zM464 544H160c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V560c0-8.8-7.2-16-16-16zm-52 268H212V612h200v200zm452-268H560c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V560c0-8.8-7.2-16-16-16zm-52 268H612V612h200v200z"
/>
</svg>
</span>
</span>
<span>
Kanban
</span>
</div>
</label>
</div>
`;

638
components/segmented/__tests__/__snapshots__/index.test.tsx.snap

@ -0,0 +1,638 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Segmented render empty segmented 1`] = `
<div
class="ant-segmented"
/>
`;
exports[`Segmented render label with ReactNode 1`] = `
<div
class="ant-segmented"
>
<label
class="ant-segmented-item ant-segmented-item-selected"
>
<input
checked=""
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Daily"
>
Daily
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
>
<div
id="weekly"
>
Weekly
</div>
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
>
<h2>
Monthly
</h2>
</div>
</label>
</div>
`;
exports[`Segmented render segmented ok 1`] = `
<div
class="ant-segmented"
>
<label
class="ant-segmented-item ant-segmented-item-selected"
>
<input
checked=""
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Daily"
>
Daily
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Weekly"
>
Weekly
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Monthly"
>
Monthly
</div>
</label>
</div>
`;
exports[`Segmented render segmented with \`block\` 1`] = `
<div
class="ant-segmented ant-segmented-block"
>
<label
class="ant-segmented-item ant-segmented-item-selected"
>
<input
checked=""
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Daily"
>
Daily
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Weekly"
>
Weekly
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Monthly"
>
Monthly
</div>
</label>
</div>
`;
exports[`Segmented render segmented with \`size#large\` 1`] = `
<div
class="ant-segmented ant-segmented-lg"
>
<label
class="ant-segmented-item ant-segmented-item-selected"
>
<input
checked=""
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Daily"
>
Daily
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Weekly"
>
Weekly
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Monthly"
>
Monthly
</div>
</label>
</div>
`;
exports[`Segmented render segmented with \`size#small\` 1`] = `
<div
class="ant-segmented ant-segmented-sm"
>
<label
class="ant-segmented-item ant-segmented-item-selected"
>
<input
checked=""
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Daily"
>
Daily
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Weekly"
>
Weekly
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Monthly"
>
Monthly
</div>
</label>
</div>
`;
exports[`Segmented render segmented with mixed options 1`] = `
<div
class="ant-segmented"
>
<label
class="ant-segmented-item ant-segmented-item-selected"
>
<input
checked=""
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Daily"
>
Daily
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Weekly"
>
Weekly
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Monthly"
>
Monthly
</div>
</label>
</div>
`;
exports[`Segmented render segmented with numeric options 1`] = `
<div
class="ant-segmented"
>
<label
class="ant-segmented-item ant-segmented-item-selected"
>
<input
checked=""
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="1"
>
1
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="2"
>
2
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="3"
>
3
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="4"
>
4
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="5"
>
5
</div>
</label>
</div>
`;
exports[`Segmented render segmented with options null/undefined 1`] = `
<div
class="ant-segmented ant-segmented-disabled"
>
<label
class="ant-segmented-item ant-segmented-item-selected"
>
<input
checked=""
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
/>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
/>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title=""
/>
</label>
</div>
`;
exports[`Segmented render segmented with options: disabled 1`] = `
<div
class="ant-segmented"
>
<label
class="ant-segmented-item ant-segmented-item-selected"
>
<input
checked=""
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Daily"
>
Daily
</div>
</label>
<label
class="ant-segmented-item ant-segmented-item-disabled"
>
<input
class="ant-segmented-item-input"
disabled=""
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Weekly"
>
Weekly
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Monthly"
>
Monthly
</div>
</label>
</div>
`;
exports[`Segmented render segmented with string options 1`] = `
<div
class="ant-segmented"
>
<label
class="ant-segmented-item ant-segmented-item-selected"
>
<input
checked=""
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Daily"
>
Daily
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Weekly"
>
Weekly
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Monthly"
>
Monthly
</div>
</label>
</div>
`;
exports[`Segmented render segmented with thumb 1`] = `
<div
class="ant-segmented"
>
<label
class="ant-segmented-item ant-segmented-item-selected"
>
<input
checked=""
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Map"
>
Map
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Transit"
>
Transit
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Satellite"
>
Satellite
</div>
</label>
</div>
`;
exports[`Segmented render segmented: disabled 1`] = `
<div
class="ant-segmented ant-segmented-disabled"
>
<label
class="ant-segmented-item ant-segmented-item-selected"
>
<input
checked=""
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Daily"
>
Daily
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Weekly"
>
Weekly
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="Monthly"
>
Monthly
</div>
</label>
</div>
`;
exports[`Segmented rtl render component should be rendered correctly in RTL direction 1`] = `
<div
class="ant-segmented ant-segmented-rtl"
/>
`;

3
components/segmented/__tests__/demo-extend.test.ts

@ -0,0 +1,3 @@
import { extendTest } from '../../../tests/shared/demoTest';
extendTest('segmented');

3
components/segmented/__tests__/demo.test.js

@ -0,0 +1,3 @@
import demoTest from '../../../tests/shared/demoTest';
demoTest('segmented');

5
components/segmented/__tests__/image.test.ts

@ -0,0 +1,5 @@
import { imageDemoTest } from '../../../tests/shared/imageTest';
describe('Segmented image', () => {
imageDemoTest('segmented');
});

360
components/segmented/__tests__/index.test.tsx

@ -0,0 +1,360 @@
import React from 'react';
import { mount } from 'enzyme';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import Segmented from '../index';
import type { SegmentedValue } from '../index';
// Make CSSMotion working without transition
jest.mock('rc-motion/lib/util/motion', () => ({
...jest.requireActual('rc-motion/lib/util/motion'),
supportTransition: false,
}));
const prefixCls = 'ant-segmented';
describe('Segmented', () => {
mountTest(Segmented);
rtlTest(Segmented);
beforeAll(() => {
jest.useFakeTimers();
});
afterAll(() => {
jest.useRealTimers();
});
it('render empty segmented', () => {
const wrapper = mount(<Segmented options={[]} />);
expect(wrapper.render()).toMatchSnapshot();
});
it('render segmented ok', () => {
const wrapper = mount(
<Segmented options={[{ label: 'Daily', value: 'Daily' }, 'Weekly', 'Monthly']} />,
);
expect(wrapper.render()).toMatchSnapshot();
expect(
wrapper
.find(`.${prefixCls}-item-input`)
.map(el => (el.getDOMNode() as HTMLInputElement).checked),
).toEqual([true, false, false]);
});
it('render label with ReactNode', () => {
const wrapper = mount(
<Segmented
options={[
{ label: 'Daily', value: 'Daily' },
{ label: <div id="weekly">Weekly</div>, value: 'Weekly' },
{ label: <h2>Monthly</h2>, value: 'Monthly' },
]}
/>,
);
expect(wrapper.render()).toMatchSnapshot();
expect(
wrapper
.find(`.${prefixCls}-item-input`)
.map(el => (el.getDOMNode() as HTMLInputElement).checked),
).toEqual([true, false, false]);
expect(wrapper.find('#weekly').at(0).text()).toContain('Weekly');
expect(wrapper.find('h2').at(0).text()).toContain('Monthly');
});
it('render segmented with defaultValue', () => {
const wrapper = mount(
<Segmented
options={['Daily', 'Weekly', 'Monthly', 'Quarterly', 'Yearly']}
defaultValue="Quarterly"
/>,
);
expect(
wrapper
.find(`.${prefixCls}-item-input`)
.map(el => (el.getDOMNode() as HTMLInputElement).checked),
).toEqual([false, false, false, true, false]);
});
it('render segmented with string options', () => {
const handleValueChange = jest.fn();
const wrapper = mount(
<Segmented
options={['Daily', 'Weekly', 'Monthly']}
onChange={e => handleValueChange(e.target.value)}
/>,
);
expect(wrapper.render()).toMatchSnapshot();
expect(
wrapper
.find(`.${prefixCls}-item-input`)
.map(el => (el.getDOMNode() as HTMLInputElement).checked),
).toEqual([true, false, false]);
expect(
wrapper.find(`.${prefixCls}-item`).at(0).hasClass(`${prefixCls}-item-selected`),
).toBeTruthy();
wrapper.find(`.${prefixCls}-item-input`).at(2).simulate('change');
expect(handleValueChange).toBeCalledWith('Monthly');
expect(
wrapper
.find(`.${prefixCls}-item-input`)
.map(el => (el.getDOMNode() as HTMLInputElement).checked),
).toEqual([false, false, true]);
});
it('render segmented with numeric options', () => {
const handleValueChange = jest.fn();
const wrapper = mount(
<Segmented options={[1, 2, 3, 4, 5]} onChange={e => handleValueChange(e.target.value)} />,
);
expect(wrapper.render()).toMatchSnapshot();
expect(
wrapper
.find(`.${prefixCls}-item-input`)
.map(el => (el.getDOMNode() as HTMLInputElement).checked),
).toEqual([true, false, false, false, false]);
wrapper.find(`.${prefixCls}-item-input`).last().simulate('change');
expect(handleValueChange).toBeCalledWith(5);
expect(
wrapper
.find(`.${prefixCls}-item-input`)
.map(el => (el.getDOMNode() as HTMLInputElement).checked),
).toEqual([false, false, false, false, true]);
});
it('render segmented with mixed options', () => {
const handleValueChange = jest.fn();
const wrapper = mount(
<Segmented
options={['Daily', { label: 'Weekly', value: 'Weekly' }, 'Monthly']}
onChange={e => handleValueChange(e.target.value)}
/>,
);
expect(wrapper.render()).toMatchSnapshot();
wrapper.find(`.${prefixCls}-item-input`).at(1).simulate('change');
expect(handleValueChange).toBeCalledWith('Weekly');
expect(
wrapper
.find(`.${prefixCls}-item-input`)
.map(el => (el.getDOMNode() as HTMLInputElement).checked),
).toEqual([false, true, false]);
});
it('render segmented with options: disabled', () => {
const handleValueChange = jest.fn();
const wrapper = mount(
<Segmented
options={['Daily', { label: 'Weekly', value: 'Weekly', disabled: true }, 'Monthly']}
onChange={e => handleValueChange(e.target.value)}
/>,
);
expect(wrapper.render()).toMatchSnapshot();
expect(
wrapper.find(`.${prefixCls}-item`).at(1).hasClass(`${prefixCls}-item-disabled`),
).toBeTruthy();
expect(wrapper.find(`.${prefixCls}-item-input`).at(1).prop('disabled')).toBeTruthy();
wrapper.find(`.${prefixCls}-item-input`).at(1).simulate('change');
expect(handleValueChange).not.toBeCalled();
expect(
wrapper
.find(`.${prefixCls}-item-input`)
.map(el => (el.getDOMNode() as HTMLInputElement).checked),
).toEqual([true, false, false]);
wrapper.find(`.${prefixCls}-item-input`).at(2).simulate('change');
expect(handleValueChange).toBeCalledWith('Monthly');
expect(handleValueChange).toBeCalledTimes(1);
expect(
wrapper
.find(`.${prefixCls}-item-input`)
.map(el => (el.getDOMNode() as HTMLInputElement).checked),
).toEqual([false, false, true]);
});
it('render segmented: disabled', () => {
const handleValueChange = jest.fn();
const wrapper = mount(
<Segmented
disabled
options={['Daily', 'Weekly', 'Monthly']}
onChange={e => handleValueChange(e.target.value)}
/>,
);
expect(wrapper.render()).toMatchSnapshot();
expect(wrapper.find(`.${prefixCls}`).hasClass(`${prefixCls}-disabled`)).toBeTruthy();
wrapper.find(`.${prefixCls}-item-input`).at(1).simulate('change');
expect(handleValueChange).not.toBeCalled();
expect(
wrapper
.find(`.${prefixCls}-item-input`)
.map(el => (el.getDOMNode() as HTMLInputElement).checked),
).toEqual([true, false, false]);
wrapper.find(`.${prefixCls}-item-input`).at(2).simulate('change');
expect(handleValueChange).not.toBeCalled();
expect(
wrapper
.find(`.${prefixCls}-item-input`)
.map(el => (el.getDOMNode() as HTMLInputElement).checked),
).toEqual([true, false, false]);
});
it('render segmented with className and other html attributes', () => {
const wrapper = mount(
<Segmented
options={['Daily', 'Monthly', 'Weekly']}
defaultValue="Weekly"
className="mock-cls"
data-test-id="hello"
/>,
);
expect(wrapper.hasClass('mock-cls')).toBeTruthy();
expect(wrapper.prop('data-test-id')).toBe('hello');
});
it('render segmented with ref', () => {
const ref = React.createRef<HTMLDivElement>();
const wrapper = mount(
<Segmented options={['Daily', 'Monthly', 'Weekly']} defaultValue="Weekly" ref={ref} />,
);
expect((wrapper.find(Segmented).getElement() as any).ref).toBe(ref);
});
it('render segmented with controlled mode', () => {
class Demo extends React.Component<{}, { value: SegmentedValue }> {
state = {
value: 'Map',
};
render() {
return (
<Segmented
options={['Map', 'Transit', 'Satellite']}
value={this.state.value}
onChange={e =>
this.setState({
value: e.target.value,
})
}
/>
);
}
}
const wrapper = mount<typeof Demo>(<Demo />);
wrapper.find('Segmented').find(`.${prefixCls}-item-input`).at(0).simulate('change');
expect(wrapper.state().value).toBe('Map');
wrapper.find('Segmented').find(`.${prefixCls}-item-input`).at(1).simulate('change');
expect(wrapper.state().value).toBe('Transit');
});
it('render segmented with options null/undefined', () => {
const handleValueChange = jest.fn();
const wrapper = mount(
<Segmented
options={[null, undefined, ''] as any}
disabled
onChange={e => handleValueChange(e.target.value)}
/>,
);
expect(wrapper.render()).toMatchSnapshot();
expect(wrapper.find(`.${prefixCls}-item-label`).map(n => n.getDOMNode().textContent)).toEqual([
'',
'',
'',
]);
});
it('render segmented with thumb', () => {
const handleValueChange = jest.fn();
const wrapper = mount(
<Segmented
options={['Map', 'Transit', 'Satellite']}
onChange={e => handleValueChange(e.target.value)}
/>,
);
expect(wrapper.render()).toMatchSnapshot();
expect(
wrapper
.find(`.${prefixCls}-item-input`)
.map(el => (el.getDOMNode() as HTMLInputElement).checked),
).toEqual([true, false, false]);
expect(
wrapper.find(`.${prefixCls}-item`).at(0).hasClass(`${prefixCls}-item-selected`),
).toBeTruthy();
wrapper.find(`.${prefixCls}-item-input`).at(2).simulate('change');
expect(handleValueChange).toBeCalledWith('Satellite');
expect(
wrapper
.find(`.${prefixCls}-item-input`)
.map(el => (el.getDOMNode() as HTMLInputElement).checked),
).toEqual([false, false, true]);
// thumb appeared
expect(wrapper.find(`.${prefixCls}-thumb`).length).toBe(1);
// change selection again
wrapper.find(`.${prefixCls}-item-input`).at(1).simulate('change');
expect(handleValueChange).toBeCalledWith('Transit');
expect(
wrapper
.find(`.${prefixCls}-item-input`)
.map(el => (el.getDOMNode() as HTMLInputElement).checked),
).toEqual([false, true, false]);
// thumb appeared
expect(wrapper.find(`.${prefixCls}-thumb`).length).toBe(1);
});
it('render segmented with `block`', () => {
const wrapper = mount(<Segmented block options={['Daily', 'Weekly', 'Monthly']} />);
expect(wrapper.render()).toMatchSnapshot();
expect(wrapper.find(`.${prefixCls}`).at(0).hasClass(`${prefixCls}-block`)).toBeTruthy();
});
it('render segmented with `size#small`', () => {
const wrapper = mount(<Segmented size="small" options={['Daily', 'Weekly', 'Monthly']} />);
expect(wrapper.render()).toMatchSnapshot();
expect(wrapper.find(`.${prefixCls}`).at(0).hasClass(`${prefixCls}-sm`)).toBeTruthy();
});
it('render segmented with `size#large`', () => {
const wrapper = mount(<Segmented size="large" options={['Daily', 'Weekly', 'Monthly']} />);
expect(wrapper.render()).toMatchSnapshot();
expect(wrapper.find(`.${prefixCls}`).at(0).hasClass(`${prefixCls}-lg`)).toBeTruthy();
});
});

33
components/segmented/demo/basic.md

@ -0,0 +1,33 @@
---
order: 0
title:
zh-CN: 基本
en-US: Basic
---
## zh-CN
最简单的用法。
## en-US
The most basic usage.
```jsx
import { Segmented } from 'antd';
ReactDOM.render(
<Segmented options={['Daily', 'Weekly', 'Monthly', 'Quarterly', 'Yearly']} />,
mountNode,
);
```
```css
.code-box-demo {
overflow-x: auto;
}
.code-box-demo .ant-segmented {
margin-bottom: 10px;
}
```

23
components/segmented/demo/block.md

@ -0,0 +1,23 @@
---
order: 10
title:
zh-CN: Block 分段选择器
en-US: Block Segmented
---
## zh-CN
`block` 属性使其适合父元素宽度。
## en-US
`block` property will make the `Segmented` fit to its parent width.
```jsx
import { Segmented } from 'antd';
ReactDOM.render(
<Segmented block options={[123, 456, 'longtext-longtext-longtext-longtext']} />,
mountNode,
);
```

33
components/segmented/demo/controlled.md

@ -0,0 +1,33 @@
---
order: 0
title:
zh-CN: 受控模式
en-US: Controlled mode
---
## zh-CN
受控的 Segmented。
## en-US
Controlled Segmented.
```jsx
import React, { useState } from 'react';
import { Segmented } from 'antd';
const Demo: React.FC = () => {
const [value, setValue] = useState('Map');
return (
<Segmented
options={['Map', 'Transit', 'Satellite']}
value={value}
onChange={e => setValue(e.target.value)}
/>
);
};
ReactDOM.render(<Demo />, mountNode);
```

97
components/segmented/demo/custom.md

@ -0,0 +1,97 @@
---
order: 1
title:
zh-CN: 自定义渲染
en-US: Custom Render
---
## zh-CN
使用 ReactNode 自定义渲染每一个 Segmented Item。
## en-US
Custom each Segmented Item by ReactNode.
```jsx
import { Avatar, Segmented } from 'antd';
import { UserOutlined } from '@ant-design/icons';
ReactDOM.render(
<>
<Segmented
options={[
{
label: (
<>
<Avatar src="https://joeschmoe.io/api/v1/random" />
<div>User 1</div>
</>
),
value: 'user1',
},
{
label: (
<>
<Avatar style={{ backgroundColor: '#f56a00' }}>K</Avatar>
<div>User 2</div>
</>
),
value: 'user2',
},
{
label: (
<>
<Avatar style={{ backgroundColor: '#87d068' }} icon={<UserOutlined />} />
<div>User 3</div>
</>
),
value: 'user3',
},
]}
/>
<br />
<Segmented
options={[
{
label: (
<>
<div>Spring</div>
<div>Jan-Mar</div>
</>
),
value: 'spring',
},
{
label: (
<>
<div>Summer</div>
<div>Apr-Jun</div>
</>
),
value: 'summer',
},
{
label: (
<>
<div>Autumn</div>
<div>Jul-Sept</div>
</>
),
value: 'autumn',
},
{
label: (
<>
<div>Winter</div>
<div>Oct-Dec</div>
</>
),
value: 'winter',
},
]}
/>
</>,
mountNode,
);
```

35
components/segmented/demo/disabled.md

@ -0,0 +1,35 @@
---
order: 0
title:
zh-CN: 不可用
en-US: Basic
---
## zh-CN
Segmented 不可用。
## en-US
Disabled Segmented.
```jsx
import { Segmented } from 'antd';
ReactDOM.render(
<>
<Segmented options={['Map', 'Transit', 'Satellite']} disabled />
<br />
<Segmented
options={[
'Daily',
{ label: 'Weekly', value: 'Weekly', disabled: true },
'Monthly',
{ label: 'Quarterly', value: 'Quarterly', disabled: true },
'Yearly',
]}
/>
</>,
mountNode,
);
```

43
components/segmented/demo/dynamic.md

@ -0,0 +1,43 @@
---
order: 0
title:
zh-CN: 动态数据
en-US: Dynamic
---
## zh-CN
动态加载数据。
## en-US
Load `options` dynamically.
```tsx
import React, { useState } from 'react';
import { Segmented, Button } from 'antd';
const defaultOptions = ['Daily', 'Weekly', 'Monthly'];
const Demo: React.FC = () => {
const [options, setOptions] = useState(defaultOptions);
const [moreLoaded, setMoreLoaded] = useState(false);
const handleLoadOptions = () => {
setOptions([...defaultOptions, 'Quarterly', 'Yearly']);
setMoreLoaded(true);
};
return (
<>
<Segmented options={options} />
<br />
<Button type="primary" disabled={moreLoaded} onClick={handleLoadOptions}>
Load more options
</Button>
</>
);
};
ReactDOM.render(<Demo />, mountNode);
```

29
components/segmented/demo/size.md

@ -0,0 +1,29 @@
---
order: 1
title:
zh-CN: 三种大小
en-US: Three sizes of Segmented
---
## zh-CN
我们为 `<Segmented />` 组件定义了三种尺寸(大、默认、小),高度分别为 `40px`、`32px` 和 `24px`
## en-US
There are three sizes of an Segmented: `large` (40px), `default` (32px) and `small` (24px).
```jsx
import { Segmented } from 'antd';
ReactDOM.render(
<>
<Segmented size="large" options={['Daily', 'Weekly', 'Monthly', 'Quarterly', 'Yearly']} />
<br />
<Segmented options={['Daily', 'Weekly', 'Monthly', 'Quarterly', 'Yearly']} />
<br />
<Segmented size="small" options={['Daily', 'Weekly', 'Monthly', 'Quarterly', 'Yearly']} />
</>,
mountNode,
);
```

37
components/segmented/demo/with-icon.md

@ -0,0 +1,37 @@
---
order: 0
title:
zh-CN: 设置图标
en-US: With Icon
---
## zh-CN
给 Segmented Item 设置 Icon。
## en-US
Set `icon` for Segmented Item.
```jsx
import { Segmented } from 'antd';
import { AppstoreOutlined, BarsOutlined } from '@ant-design/icons';
ReactDOM.render(
<Segmented
options={[
{
label: 'List',
value: 'List',
icon: <BarsOutlined />,
},
{
label: 'Kanban',
value: 'Kanban',
icon: <AppstoreOutlined />,
},
]}
/>,
mountNode,
);
```

27
components/segmented/index.en-US.md

@ -0,0 +1,27 @@
---
category: Components
type: Data Display
title: Segmented
cover: https://gw.alipayobjects.com/zos/bmw-prod/a3ff040f-24ba-43e0-92e9-c845df1612ad.svg
---
Segmented Controls. This component is available since `antd@4.20.0`.
## When To Use
- When displaying multiple options and user can select a single option;
- When switching the selected option, the content of the associated area changes.
## API
> This component is available since `antd@4.20.0`
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| block | Option to fit width to its parent\'s width | boolean | false | |
| defaultValue | Default selected value | string \| number | | |
| disabled | Disable all segments | boolean | false | |
| onChange | The callback function that is triggered when the state changes | function(e:Event) | | |
| options | Set children optional | string\[] \| number\[] \| Array<{ label: string value: string icon? ReactNode disabled?: boolean className?: string }> | [] | |
| size | The size of the Segmented. | `large` \| `middle` \| `small` | - | |
| value | Currently selected value | string \| number | | |

87
components/segmented/index.tsx

@ -0,0 +1,87 @@
import * as React from 'react';
import classNames from 'classnames';
import RcSegmented from 'rc-segmented';
import type {
SegmentedProps as RCSegmentedProps,
SegmentedRawOption,
SegmentedLabeledOption as RcSegmentedLabeledOption,
} from 'rc-segmented';
import { ConfigContext } from '../config-provider';
import SizeContext, { SizeType } from '../config-provider/SizeContext';
export type { SegmentedValue } from 'rc-segmented';
export interface SegmentedLabeledOption extends RcSegmentedLabeledOption {
/** Set icon for Segmented item */
icon?: React.ReactNode;
}
export interface SegmentedProps extends Omit<RCSegmentedProps, 'size'> {
options: (SegmentedRawOption | SegmentedLabeledOption)[];
/** Option to fit width to its parent's width */
block?: boolean;
/** Option to control the display size */
size?: SizeType;
}
const Segmented = React.forwardRef<HTMLDivElement, SegmentedProps>((props, ref) => {
const {
prefixCls: customizePrefixCls,
className,
block,
options,
size: customSize = 'middle',
...restProps
} = props;
const { getPrefixCls, direction } = React.useContext(ConfigContext);
const prefixCls = getPrefixCls('segmented', customizePrefixCls);
// ===================== Size =====================
const size = React.useContext(SizeContext);
const mergedSize = customSize || size;
// syntactic sugar to support `icon` for Segmented Item
const extendedOptions = React.useMemo(
() =>
options.map(option => {
if (typeof option === 'object' && option?.icon) {
const { icon, label, ...restOption } = option;
return {
...restOption,
label: (
<>
<span className={`${prefixCls}-item-icon`}>{icon}</span>
<span>{label}</span>
</>
),
};
}
return option;
}),
[options, prefixCls],
);
return (
<RcSegmented
{...restProps}
className={classNames(className, {
[`${prefixCls}-block`]: block,
[`${prefixCls}-sm`]: mergedSize === 'small',
[`${prefixCls}-lg`]: mergedSize === 'large',
})}
options={extendedOptions}
ref={ref}
prefixCls={prefixCls}
direction={direction}
/>
);
});
Segmented.displayName = 'Segmented';
Segmented.defaultProps = {
options: [],
};
export default Segmented;

30
components/segmented/index.zh-CN.md

@ -0,0 +1,30 @@
---
category: Components
subtitle: 分段控制器
type: 数据展示
title: Segmented
cover: https://gw.alipayobjects.com/zos/bmw-prod/a3ff040f-24ba-43e0-92e9-c845df1612ad.svg
---
分段控制器。自 `antd@4.20.0` 版本开始提供该组件。
## 何时使用
- 用于展示多个选项并允许用户选择其中单个选项;
- 当切换选中选项时,关联区域的内容会发生变化。
## API
> 自 `antd@4.20.0` 版本开始提供该组件。
### Segmented
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| block | 将宽度调整为父元素宽度的选项 | boolean | false | |
| defaultValue | 默认选中的值 | string \| number | | |
| disabled | 是否禁用 | boolean | false | |
| onChange | 选项变化时的回调函数 | function(e:Event) | | |
| options | 数据化配置选项内容 | string\[] \| number\[] \| Array<{ label: string value: string icon? ReactNode disabled?: boolean className?: string }> | [] | |
| size | 控件尺寸 | `large` \| `middle` \| `small` | - | |
| value | 当前选中的值 | string \| number | | |

116
components/segmented/style/index.less

@ -0,0 +1,116 @@
@import '../../style/themes/index';
@import '../../style/mixins/index';
@import './mixins.less';
@segmented-prefix-cls: ~'@{ant-prefix}-segmented';
.@{segmented-prefix-cls} {
.reset-component();
position: relative;
display: inline-flex;
align-items: stretch;
justify-items: flex-start;
color: @segmented-label-color;
background-color: @segmented-bg;
border-radius: 2px;
box-shadow: 0 0 0 2px @segmented-bg;
transition: all 0.3s @ease-in-out;
// hover/focus styles
&:not(&-disabled) {
&:hover,
&:focus {
background-color: @segmented-hover-bg;
box-shadow: 0 0 0 2px @segmented-hover-bg;
}
}
// block styles
&&-block {
display: flex;
}
&&-block &-item {
flex: 1;
min-width: 0;
}
// item styles
&-item {
position: relative;
text-align: center;
cursor: pointer;
transition: color 0.3s @ease-in-out;
&-selected {
.segmented-item-selected();
color: @segmented-label-hover-color;
}
&:hover,
&:focus {
color: @segmented-label-hover-color;
}
&-label {
min-height: @input-height-base;
padding: @input-padding-vertical-base @input-padding-horizontal-base;
line-height: @input-height-base - @input-padding-vertical-base * 2;
.segmented-text-ellipsis();
}
// syntactic sugar to add `icon` for Segmented Item
&-icon {
margin-right: 6px;
}
&-input {
position: absolute;
top: 0;
left: 0;
width: 0;
height: 0;
opacity: 0;
pointer-events: none;
}
}
// size styles
&&-lg &-item-label {
min-height: @input-height-lg;
padding: @input-padding-vertical-lg @input-padding-horizontal-lg;
font-size: @font-size-lg;
line-height: @input-height-lg - @input-padding-vertical-lg * 2;
}
&&-sm &-item-label {
min-height: @input-height-sm;
padding: @input-padding-vertical-sm @input-padding-horizontal-sm;
line-height: @input-height-sm - @input-padding-vertical-sm * 2;
}
// disabled styles
&-disabled &-item,
&-item-disabled {
.segmented-disabled-item();
}
// thumb styles
&-thumb {
.segmented-item-selected();
position: absolute;
top: 0;
left: 0;
width: 0;
height: 100%;
padding: 4px 0;
}
// transition effect when `enter-active`
&-thumb-motion-enter-active {
transition: transform 0.3s @ease-in-out, width 0.3s @ease-in-out;
will-change: transform, width;
}
}

2
components/segmented/style/index.tsx

@ -0,0 +1,2 @@
import '../../style/index.less';
import './index.less';

24
components/segmented/style/mixins.less

@ -0,0 +1,24 @@
// mixins
.segmented-disabled-item {
&,
&:hover,
&:focus {
color: @disabled-color;
cursor: not-allowed;
}
}
.segmented-item-selected {
background-color: @segmented-selected-bg;
border-radius: @border-radius-base;
box-shadow: 0 2px 8px -2px fade(@black, 5%), 0 1px 4px -1px fade(@black, 7%),
0 0 1px 0 fade(@black, 8%);
}
.segmented-text-ellipsis {
overflow: hidden;
// handle text ellipsis
white-space: nowrap;
text-overflow: ellipsis;
word-break: keep-all;
}

8
components/style/themes/dark.less

@ -447,3 +447,11 @@
// Mentions
// ---
@mentions-dropdown-bg: @popover-background;
// Segmented
// ---
@segmented-bg: fade(@black, 25%);
@segmented-hover-bg: fade(@black, 45%);
@segmented-selected-bg: #333333;
@segmented-label-color: fade(@white, 65%);
@segmented-label-hover-color: fade(@white, 85%);

8
components/style/themes/default.less

@ -1070,3 +1070,11 @@
@image-preview-operation-size: 18px;
@image-preview-operation-color: @text-color-dark;
@image-preview-operation-disabled-color: fade(@image-preview-operation-color, 25%);
// Segmented
// ---
@segmented-bg: fade(@black, 4%);
@segmented-hover-bg: fade(@black, 6%);
@segmented-selected-bg: @white;
@segmented-label-color: fade(@black, 65%);
@segmented-label-hover-color: #262626;

8
components/style/themes/variable.less

@ -1125,3 +1125,11 @@ html {
@image-preview-operation-size: 18px;
@image-preview-operation-color: @text-color-dark;
@image-preview-operation-disabled-color: fade(@image-preview-operation-color, 25%);
// Segmented
// ---
@segmented-bg: fade(@black, 4%);
@segmented-hover-bg: fade(@black, 6%);
@segmented-selected-bg: @white;
@segmented-label-color: fade(@black, 65%);
@segmented-label-hover-color: #262626;

3
package.json

@ -142,6 +142,7 @@
"rc-progress": "~3.2.1",
"rc-rate": "~2.9.0",
"rc-resize-observer": "^1.2.0",
"rc-segmented": "^1.0.0",
"rc-select": "~14.0.2",
"rc-slider": "~10.0.0-alpha.4",
"rc-steps": "~4.1.0",
@ -316,7 +317,7 @@
},
{
"path": "./dist/antd.variable.min.css",
"maxSize": "66 kB"
"maxSize": "67 kB"
}
],
"tnpm": {

1
tests/__snapshots__/index.test.js.snap

@ -45,6 +45,7 @@ Array [
"Rate",
"Result",
"Row",
"Segmented",
"Select",
"Skeleton",
"Slider",

Loading…
Cancel
Save