-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
bbc61b6
commit 9526b37
Showing
5 changed files
with
254 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
import type { Meta, StoryObj } from '@storybook/react'; | ||
import React, { use, useState } from 'react'; | ||
import classNames from 'classnames'; | ||
|
||
import { Spinner } from './Spinner'; | ||
|
||
const meta = { | ||
title: 'Components/Spinner', | ||
component: Spinner, | ||
tags: ['autodocs'], | ||
parameters: { | ||
controls: { expanded: true }, | ||
}, | ||
} satisfies Meta<typeof Spinner>; | ||
|
||
export default meta; | ||
|
||
type Story = StoryObj<typeof Spinner>; | ||
|
||
/** | ||
* デフォルトのサイズ一覧です<br /> | ||
* <b>show code</b>ボタンからコードを確認できます。 | ||
*/ | ||
export const Default: Story = { | ||
render: () => ( | ||
<div className="p-16 grid grid-cols-4 w-fit"> | ||
<Spinner variant="primary" size="sm" /> | ||
<Spinner variant="primary" size="md" /> | ||
<Spinner variant="primary" size="lg" /> | ||
<Spinner variant="primary" size="xl" /> | ||
</div> | ||
), | ||
}; | ||
|
||
/** | ||
* ボタンと一緒に表示するサンプルです。<br /> | ||
* DaisyUIのloading loading-spinnerクラスでも同じような事ができます。<br /> | ||
* <b>show code</b>ボタンからコードを確認できます。 | ||
*/ | ||
export const Loading: Story = { | ||
render: () => { | ||
// eslint-disable-next-line react-hooks/rules-of-hooks | ||
const [isLoading, setIsLoading] = useState(false); | ||
// eslint-disable-next-line react-hooks/rules-of-hooks | ||
const [data, setData] = useState<null | string>(null); | ||
|
||
const handleClick = async () => { | ||
setIsLoading(true); | ||
const res = await new Promise<string>(resolve => | ||
setTimeout(() => resolve('Fetch Data!'), 3000), | ||
); | ||
setData(res); | ||
setIsLoading(false); | ||
}; | ||
|
||
return ( | ||
<div className="p-16 grid grid-rows-2 gap-1 border-2 border-gray-300 rounded-3xl h-fit w-[600px]"> | ||
<h1 className="text-4xl font-bold text-green-500 h-fit">{data}</h1> | ||
<button | ||
className={classNames('btn btn-primary pw-4 font-bold text-lg', { | ||
'pointer-events-none disabled ': isLoading, | ||
})} | ||
onClick={handleClick}> | ||
{isLoading ? <span className="loading loading-spinner"></span> : null}{' '} | ||
Show | ||
{isLoading ? <Spinner variant="primary" size="md" /> : null} | ||
</button> | ||
</div> | ||
); | ||
}, | ||
}; | ||
|
||
/** | ||
* Suspenseを使ったサンプルです。<br /> | ||
* Storybook上でNext.jsのServer ComponentsでAxios使う方法の変わりにuse Hooksを使っています。<br /> | ||
* <b>show code</b>ボタンからコードを確認できます。 | ||
*/ | ||
export const Suspense: Story = { | ||
render: () => { | ||
const APIRequest = new Promise<string>(resolve => | ||
setTimeout(() => resolve('Fetch Data!'), 3000), | ||
); | ||
const FetchMessage = ({ APIRequest }: { APIRequest: Promise<string> }) => { | ||
const res = use(APIRequest); | ||
return ( | ||
<div className="w-fit"> | ||
<h1 className="text-6xl font-bold text-green-500">{res}</h1> | ||
</div> | ||
); | ||
}; | ||
|
||
return ( | ||
<div className="p-16 grid place-content-center border-2 border-gray-300 rounded-3xl h-[192px] w-[600px]"> | ||
<React.Suspense fallback={<Spinner variant="primary" size="md" />}> | ||
<FetchMessage APIRequest={APIRequest} /> | ||
</React.Suspense> | ||
</div> | ||
); | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
import React from 'react'; | ||
|
||
import { render } from '@testing-library/react'; | ||
import { Spinner } from './Spinner'; | ||
|
||
// Generated by CodiumAI | ||
|
||
describe('Spinner', () => { | ||
// Renders a spinner with default size and variant | ||
test('デフォルトのサイズとバリアントでスピナーをレンダリングする', () => { | ||
const { container } = render(<Spinner />); | ||
expect(container).toBeInTheDocument(); | ||
}); | ||
|
||
// Renders a spinner with custom size and variant | ||
test('カスタムのサイズとバリアントでスピナーをレンダリングする', () => { | ||
const { container } = render(<Spinner size="lg" variant="light" />); | ||
expect(container).toBeInTheDocument(); | ||
}); | ||
|
||
// Renders a spinner with no className | ||
test('classNameなしでスピナーをレンダリングする', () => { | ||
const { container } = render(<Spinner />); | ||
expect(container).toBeInTheDocument(); | ||
}); | ||
|
||
// Renders a spinner with data-testid attribute | ||
test('data-testid属性を持つスピナーをレンダリングする', () => { | ||
const { getByTestId } = render(<Spinner />); | ||
expect(getByTestId('loading')).toBeInTheDocument(); | ||
}); | ||
|
||
// Renders a spinner with size set to 'lg' | ||
test('サイズを「lg」に設定したスピナーをレンダリングする', () => { | ||
const { container } = render(<Spinner size="lg" />); | ||
expect(container).toBeInTheDocument(); | ||
}); | ||
|
||
// Renders a spinner with size set to 'sm' | ||
test('サイズを「sm」に設定したスピナーをレンダリングする', () => { | ||
const { container } = render(<Spinner size="sm" />); | ||
expect(container).toBeInTheDocument(); | ||
}); | ||
|
||
// Renders a spinner with variant set to 'light' | ||
test('バリアントを「light」に設定したスピナーをレンダリングする', () => { | ||
const { container } = render(<Spinner variant="light" />); | ||
expect(container).toBeInTheDocument(); | ||
}); | ||
|
||
// test passing all variant and size | ||
test('全てのvariantsとsizesでレンダリングする', () => { | ||
// Render the Spinner component with all variants and sizes | ||
const { container } = render( | ||
<> | ||
<Spinner variant="light" size="lg" /> | ||
<Spinner variant="primary" size="md" /> | ||
<Spinner variant="light" size="sm" /> | ||
<Spinner variant="primary" size="xl" /> | ||
</>, | ||
); | ||
|
||
// Assert that the Spinner components are rendered with the correct SVG elements | ||
const svgs = container.querySelectorAll('svg'); | ||
expect(svgs.length).toBe(4); | ||
expect(svgs[0].getAttribute('class')).toBe( | ||
'animate-spin h-16 w-16 text-white', | ||
); | ||
expect(svgs[1].getAttribute('class')).toBe( | ||
'animate-spin h-8 w-8 text-blue-200', | ||
); | ||
expect(svgs[2].getAttribute('class')).toBe( | ||
'animate-spin h-4 w-4 text-white', | ||
); | ||
expect(svgs[3].getAttribute('class')).toBe( | ||
'animate-spin h-24 w-24 text-blue-200', | ||
); | ||
|
||
// Assert that the Spinner components are rendered with the correct circle and path elements | ||
const circles = container.querySelectorAll('circle'); | ||
const paths = container.querySelectorAll('path'); | ||
expect(circles.length).toBe(4); | ||
expect(paths.length).toBe(4); | ||
|
||
// Assert that the Spinner components are rendered with the correct span element | ||
const spans = container.querySelectorAll('span'); | ||
expect(spans.length).toBe(4); | ||
expect(spans[0].getAttribute('class')).toBe('sr-only'); | ||
expect(spans[0].textContent).toBe('Loading'); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import classNames from 'classnames'; | ||
import React, { memo } from 'react'; | ||
|
||
const sizes = { | ||
lg: 'h-16 w-16', | ||
md: 'h-8 w-8', | ||
sm: 'h-4 w-4', | ||
xl: 'h-24 w-24', | ||
}; | ||
|
||
const variants = { | ||
light: 'text-white', | ||
primary: 'text-blue-200', | ||
}; | ||
|
||
export type SpinnerProps = { | ||
className?: string; | ||
size?: keyof typeof sizes; | ||
variant?: keyof typeof variants; | ||
}; | ||
|
||
/** | ||
* ロード中などに表示するSpinnerです。<br /> | ||
* lg、md、sm, xsのサイズがあります。<br /> | ||
* いまのところdark themeがないので、variantのlightは使わないと思います。 | ||
* | ||
* | ||
*/ | ||
export const Spinner: React.FC<React.PropsWithChildren<SpinnerProps>> = memo( | ||
({ className = '', size = 'md', variant = 'primary' }: SpinnerProps) => { | ||
return ( | ||
<> | ||
<svg | ||
className={classNames( | ||
'animate-spin', | ||
sizes[size], | ||
variants[variant], | ||
className, | ||
)} | ||
xmlns="http://www.w3.org/2000/svg" | ||
fill="none" | ||
viewBox="0 0 24 24" | ||
data-testid="loading"> | ||
<circle | ||
className="opacity-25" | ||
cx="12" | ||
cy="12" | ||
r="10" | ||
stroke="currentColor" | ||
strokeWidth="4"></circle> | ||
<path | ||
className="opacity-75" | ||
fill="currentColor" | ||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path> | ||
</svg> | ||
<span className="sr-only">Loading</span> | ||
</> | ||
); | ||
}, | ||
); | ||
Spinner.displayName = 'Spinner'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './Spinner'; |