Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/kufu/smarthr-ui into chor…
Browse files Browse the repository at this point in the history
…e-refactoring-ComboBox-useOptions
  • Loading branch information
AtsushiM committed Feb 6, 2025
2 parents ffb3ad7 + d514235 commit 0a7be39
Show file tree
Hide file tree
Showing 66 changed files with 3,115 additions and 2,138 deletions.
1 change: 1 addition & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ commands:
# fallback to using the latest cache if no exact match is found
- modules-cache-
- run: |
sudo npm install -g corepack@latest
sudo corepack enable
sudo corepack prepare
pnpm install
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/previewRelease.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ jobs:
node-version: 20
- name: install dependencies
run: |
npm install -g corepack@latest
corepack enable
corepack prepare
pnpm ui install
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/publishRelease.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ jobs:
git config user.name "github-actions[bot]"
- name: pnpm install
run: |
npm install -g corepack@latest
corepack enable
corepack prepare
pnpm ui install
Expand Down
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"commitmsg": "commitlint -e $GIT_PARAMS",
"prepare": "husky"
},
"packageManager": "pnpm@9.15.4",
"packageManager": "pnpm@10.2.0",
"pnpm": {
"overrides": {
"@babel/helper-compilation-targets": "^7.26.5",
Expand All @@ -37,7 +37,6 @@
"react-dom": "^19.0.0",
"micromatch@<4.0.8": ">=4.0.8",
"cross-spawn@<7.0.5": ">=7.0.5",
"tar@<6.2.1": ">=6.2.1",
"nanoid": "3.3.8"
}
}
Expand Down
3 changes: 3 additions & 0 deletions packages/smarthr-ui/.storybook/preview-head.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,7 @@
code {
font-family: 'Courier New', Courier, monospace;
}
html, body {
height: 100%;
}
</style>
23 changes: 23 additions & 0 deletions packages/smarthr-ui/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,29 @@

All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.

### [65.0.1](https://github.com/kufu/smarthr-ui/compare/v65.0.0...v65.0.1) (2025-02-04)


### Bug Fixes

* aria-describedbyがinputに紐づいている場合もFormControlのaria-describedbyを設定する ([#5344](https://github.com/kufu/smarthr-ui/issues/5344)) ([3c636f1](https://github.com/kufu/smarthr-ui/commit/3c636f109f9a93ba3e02663442a065d749473e2f))
* SegmentedControl内の選択済み項目でButton[variant=primary]を利用する ([#5310](https://github.com/kufu/smarthr-ui/issues/5310)) ([d286b90](https://github.com/kufu/smarthr-ui/commit/d286b9061bb232c23560b5700b6c8a1960ea1053))

## [65.0.0](https://github.com/kufu/smarthr-ui/compare/v64.0.1...v65.0.0) (2025-01-28)


### ⚠ BREAKING CHANGES

* FloatAreaのresponseMessage属性の型を実装に合わせて調整する (#5299)

### Bug Fixes

* **AppHeader:** AppNaviを使うように修正 ([#5296](https://github.com/kufu/smarthr-ui/issues/5296)) ([f71c333](https://github.com/kufu/smarthr-ui/commit/f71c333bbd7a133e652408cafc25bdcc2a2355da))
* **Button:** テキストのラベルが存在しない場合に自動的にsquareにする ([#5294](https://github.com/kufu/smarthr-ui/issues/5294)) ([b92fb8a](https://github.com/kufu/smarthr-ui/commit/b92fb8aaeb2ce7deee51415e05a1b77c068fe4ef))
* FloatAreaのresponseMessage属性の型を実装に合わせて調整する ([#5299](https://github.com/kufu/smarthr-ui/issues/5299)) ([0fd3afa](https://github.com/kufu/smarthr-ui/commit/0fd3afaf17f22e4b8bc7eb270a92d02780519896))
* FormControl、FieldsetでdangerouslyTitleHiddenを指定した場合、多くのtest用ライブラリで用意されているメソッドで対象にできなくなってしまう問題に対応したい ([#5339](https://github.com/kufu/smarthr-ui/issues/5339)) ([09fedd9](https://github.com/kufu/smarthr-ui/commit/09fedd96dac1dce378c44aa3e4a254ac3b7fddf0))
* **Textarea:** autoResize時にvalue, defaultValueの初期値でも高さを反映するように修正 ([#5330](https://github.com/kufu/smarthr-ui/issues/5330)) ([8d09ffa](https://github.com/kufu/smarthr-ui/commit/8d09ffaf3674d90bfaf1cd30cbedbaf0f6eeb573))

### [64.0.1](https://github.com/kufu/smarthr-ui/compare/v64.0.0...v64.0.1) (2025-01-21)


Expand Down
12 changes: 6 additions & 6 deletions packages/smarthr-ui/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "smarthr-ui",
"description": "SmartHR ui components built with React.",
"version": "64.0.1",
"version": "65.0.1",
"author": "SmartHR-UI Team",
"dependencies": {
"@smarthr/wareki": "^1.3.0",
Expand All @@ -17,8 +17,8 @@
"tailwindcss": "^3.4.17"
},
"devDependencies": {
"@babel/core": "^7.26.0",
"@babel/preset-env": "^7.26.0",
"@babel/core": "^7.26.7",
"@babel/preset-env": "^7.26.7",
"@babel/preset-react": "^7.26.3",
"@babel/preset-typescript": "^7.26.0",
"@storybook/addon-a11y": "^8.4.7",
Expand All @@ -39,12 +39,12 @@
"@storybook/test": "^8.4.7",
"@storybook/test-runner": "^0.21.0",
"@storybook/theming": "^8.4.7",
"@swc/core": "^1.10.8",
"@swc/core": "^1.10.12",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.1.0",
"@types/lodash.merge": "^4.6.9",
"@types/lodash.range": "^3.2.9",
"@types/node": "^20.17.14",
"@types/node": "^20.17.16",
"@types/react": "^18.3.18",
"@types/react-dom": "^19.0.3",
"@types/react-test-renderer": "^19.0.0",
Expand Down Expand Up @@ -84,7 +84,7 @@
"ts-loader": "^9.5.2",
"ttypescript": "^1.5.15",
"typescript-plugin-styled-components": "^3.0.0",
"vitest": "^3.0.2",
"vitest": "^3.0.4",
"wait-on": "^8.0.2",
"webpack": "^5.97.1"
},
Expand Down
2 changes: 1 addition & 1 deletion packages/smarthr-ui/public/exports/smarthr-ui-props.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions packages/smarthr-ui/src/components/Button/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export const Button = forwardRef<HTMLButtonElement, BaseProps & ElementProps & P
{
type = 'button',
size = 'default',
square,
square = false,
prefix,
suffix,
wide = false,
Expand Down Expand Up @@ -94,7 +94,7 @@ export const Button = forwardRef<HTMLButtonElement, BaseProps & ElementProps & P
className={wrapperStyle}
buttonRef={ref}
disabled={disabledOnLoading}
loading={loading}
$loading={loading}
>
{
// `button` 要素内で live region を使うことはできないので、`role="status"` を持つ要素を外側に配置している。 https://github.com/kufu/smarthr-ui/pull/4558
Expand Down
20 changes: 8 additions & 12 deletions packages/smarthr-ui/src/components/Button/ButtonWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,16 @@ import React, {
ReactNode,
useMemo,
} from 'react'
import innerText from 'react-innertext'
import { tv } from 'tailwind-variants'

import { Variant } from './types'

type BaseProps = {
size: 'default' | 's'
square?: boolean
square: boolean
wide: boolean
variant: Variant
loading?: boolean
$loading?: boolean
className: string
children: ReactNode
elementAs?: ElementType
Expand All @@ -39,27 +38,24 @@ export function ButtonWrapper({
size,
square,
wide = false,
loading,
$loading,
className,
...props
}: Props) {
const actualSquare = useMemo(() => square ?? !innerText(props.children), [props.children, square])
const { buttonStyle, anchorStyle } = useMemo(() => {
const { default: defaultButton, anchor } = button({
variant,
size,
square: actualSquare,
loading,
square,
loading: $loading,
wide,
})

const commonAttr = { className }

return {
buttonStyle: defaultButton(commonAttr),
anchorStyle: anchor(commonAttr),
buttonStyle: defaultButton({ className }),
anchorStyle: anchor({ className }),
}
}, [loading, className, size, actualSquare, variant, wide])
}, [$loading, className, size, square, variant, wide])

if (props.isAnchor) {
const { anchorRef, elementAs, isAnchor: _, ...others } = props
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { useOuterClick } from '../../../hooks/useOuterClick'
import { genericsForwardRef } from '../../../libs/util'
import { textColor } from '../../../themes'
import { FaCaretDownIcon } from '../../Icon'
import { areComboBoxItemsEqual } from '../comboBoxHelper'
import { useFocusControl } from '../useFocusControl'
import { useListBox } from '../useListBox'
import { useMultiOptions } from '../useOptions'
Expand Down Expand Up @@ -198,9 +199,7 @@ const ActualMultiComboBox = <T,>(
if (onDelete) onDelete(item)
if (onChangeSelected)
onChangeSelected(
selectedItems.filter(
(selected) => selected.label !== item.label || selected.value !== item.value,
),
selectedItems.filter((selected) => !areComboBoxItemsEqual(selected, item)),
)
})
},
Expand All @@ -211,8 +210,8 @@ const ActualMultiComboBox = <T,>(
// HINT: Dropdown系コンポーネント内でComboBoxを使うと、選択肢がportalで表現されている関係上Dropdownが閉じてしまう
// requestAnimationFrameを追加、処理を遅延させることで正常に閉じる/閉じないの判定を行えるようにする
requestAnimationFrame(() => {
const matchedSelectedItem = selectedItems.find(
(item) => item.label === selected.label && item.value === selected.value,
const matchedSelectedItem = selectedItems.find((item) =>
areComboBoxItemsEqual(item, selected),
)
if (matchedSelectedItem !== undefined) {
if (matchedSelectedItem.deletable !== false) {
Expand Down Expand Up @@ -468,7 +467,7 @@ const ActualMultiComboBox = <T,>(
className={selectedListStyle}
>
{selectedItems.map((selectedItem, i) => (
<li key={`${selectedItem.label}-${selectedItem.value}`}>
<li key={`${selectedItem.label}-${innerText(selectedItem.value)}`}>
<MultiSelectedItem
item={selectedItem}
disabled={disabled}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ describe('useMultiOptions', () => {
expect(options[0].item).toEqual({ label: labelElement3, value: 'value3' })
})

it('isItemSelectedが渡されていなくてoptionのインスタンスが違うとき、selectedにならないこと', () => {
it('isItemSelectedが渡されていなくてvalueが同じかつlabelのインスタンスが違うとき、selectedになること', () => {
const newLabelElement1 = (
<div>
label<span>1</span>
Expand All @@ -250,7 +250,7 @@ describe('useMultiOptions', () => {

expect(options.length).toBe(1)
expect(options[0].item).toEqual({ label: labelElement1, value: 'value1' })
expect(options[0].selected).toBeFalsy()
expect(options[0].selected).toBeTruthy()
expect(options[0].isNew).toBeFalsy()
})
})
Expand Down
8 changes: 8 additions & 0 deletions packages/smarthr-ui/src/components/ComboBox/comboBoxHelper.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import innerText from 'react-innertext'

import { ComboBoxItem } from './types'

export function convertMatchableString(original: string) {
return (
original
Expand All @@ -13,3 +17,7 @@ export function convertMatchableString(original: string) {
.toLowerCase()
)
}

export function areComboBoxItemsEqual<T>(a: ComboBoxItem<T>, b: ComboBoxItem<T>) {
return a.value === b.value && innerText(a.label) === innerText(b.label)
}
10 changes: 3 additions & 7 deletions packages/smarthr-ui/src/components/ComboBox/useOptions.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
import { useCallback, useId, useMemo } from 'react'
import innerText from 'react-innertext'

import { convertMatchableString } from './comboBoxHelper'
import { areComboBoxItemsEqual, convertMatchableString } from './comboBoxHelper'
import { ComboBoxItem, ComboBoxOption } from './types'

const defaultIsItemSelected = <T>(
targetItem: ComboBoxItem<T>,
selectedItems: Array<ComboBoxItem<T>>,
) =>
selectedItems.find(
(selectedItem) =>
selectedItem.label === targetItem.label && selectedItem.value === targetItem.value,
) !== undefined
) => selectedItems.some((item) => areComboBoxItemsEqual(item, targetItem))

type BaseUseOptionsProps<T> = {
items: Array<ComboBoxItem<T>>
Expand All @@ -27,7 +23,7 @@ export const useSingleOptions = <T>({
selected: ComboBoxItem<T> | null
}) => {
const isSelected = useCallback(
(item: ComboBoxItem<T>) => selected !== null && selected.label === item.label,
(item: ComboBoxItem<T>) => selected !== null && areComboBoxItemsEqual(selected, item),
[selected],
)

Expand Down
48 changes: 23 additions & 25 deletions packages/smarthr-ui/src/components/ComboBox/usePartialRendering.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react'

const OPTION_INCREMENT_AMOUNT = 100
const RETURN_NULL = () => null

export function usePartialRendering<T>({
items,
Expand All @@ -9,37 +10,31 @@ export function usePartialRendering<T>({
items: T[]
minLength?: number
}) {
const [currentItemLength, setCurrentItemLength] = useState(
Math.max(OPTION_INCREMENT_AMOUNT, minLength),
)
// minLength も考慮した実際のアイテム数を算出
const actualLength = useMemo(
() => Math.max(currentItemLength, minLength),
[currentItemLength, minLength],
)
const partialItems = useMemo(() => items.slice(0, actualLength), [actualLength, items])
const limiter = useCallback((length: number) => Math.max(length, minLength), [minLength])

useEffect(() => {
// currentItemLength を実際の値に補正
setCurrentItemLength(actualLength)
}, [actualLength])
const [currentItemLength, setCurrentItemLength] = useState(limiter(OPTION_INCREMENT_AMOUNT))

const isAllItemsShown = useMemo(() => actualLength >= items.length, [actualLength, items.length])
useEffect(() => {
setCurrentItemLength((current) => limiter(current))
}, [limiter])

const handleIntersect = useCallback(() => {
setCurrentItemLength((current) => current + OPTION_INCREMENT_AMOUNT)
}, [])
// minLength も考慮した実際のアイテム数を算出
const partialItems = useMemo(() => items.slice(0, currentItemLength), [currentItemLength, items])

const renderIntersection = useCallback(() => {
if (isAllItemsShown) {
return null
}
return <Intersection onIntersect={handleIntersect} />
}, [handleIntersect, isAllItemsShown])
const renderIntersection = useCallback(
() => (
<Intersection
onIntersect={() => {
setCurrentItemLength((current) => limiter(current + OPTION_INCREMENT_AMOUNT))
}}
/>
),
[limiter],
)

return {
items: partialItems,
renderIntersection,
renderIntersection: currentItemLength >= items.length ? RETURN_NULL : renderIntersection,
}
}

Expand All @@ -48,9 +43,11 @@ const Intersection: FC<{ onIntersect: () => void }> = ({ onIntersect }) => {

useEffect(() => {
const target = ref.current

if (target === null) {
return
}

// スクロール最下部に到達する度に表示するアイテム数を増加させるための IntersectionObserver
const observer = new IntersectionObserver(([entry]) => {
if (entry.isIntersecting) {
Expand All @@ -59,8 +56,9 @@ const Intersection: FC<{ onIntersect: () => void }> = ({ onIntersect }) => {
})

observer.observe(target)

return () => observer.disconnect()
}, [onIntersect])

return <div ref={ref}></div>
return <div ref={ref} />
}
Loading

0 comments on commit 0a7be39

Please sign in to comment.