diff --git a/superset-frontend/package.json b/superset-frontend/package.json index 634e355dd50..9e1172631ab 100644 --- a/superset-frontend/package.json +++ b/superset-frontend/package.json @@ -26,7 +26,7 @@ "prettier": "npm run format", "check-translation": "prettier --check ../superset/translations/**/LC_MESSAGES/*.json", "clean-translation": "prettier --write ../superset/translations/**/LC_MESSAGES/*.json", - "storybook": "NODE_ENV=development BABEL_ENV=development start-storybook -p 6006", + "storybook": "NODE_ENV=development BABEL_ENV=development start-storybook -s ./images -p 6006", "build-storybook": "build-storybook" }, "repository": { diff --git a/superset-frontend/src/components/Loading/Loading.stories.tsx b/superset-frontend/src/components/Loading/Loading.stories.tsx new file mode 100644 index 00000000000..b0d8bf11f0e --- /dev/null +++ b/superset-frontend/src/components/Loading/Loading.stories.tsx @@ -0,0 +1,83 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import React from 'react'; +import Loading, { Props, PositionOption } from './index'; + +export default { + title: 'Loading', + component: Loading, + includeStories: ['LoadingGallery', 'InteractiveLoading'], +}; + +export const POSITIONS: PositionOption[] = ['normal', 'floating', 'inline']; + +export const LoadingGallery = () => ( + <> + {POSITIONS.map(position => ( +
+

{position}

+ +
+ ))} + +); + +LoadingGallery.story = { + parameters: { + actions: { + disabled: true, + }, + controls: { + disabled: true, + }, + knobs: { + disabled: true, + }, + }, +}; + +export const InteractiveLoading = (args: Props) => ; + +InteractiveLoading.story = { + parameters: { + knobs: { + disabled: true, + }, + }, +}; + +InteractiveLoading.args = { + image: '/images/loading.gif', + className: '', +}; + +InteractiveLoading.argTypes = { + position: { + name: 'position', + control: { type: 'select', options: POSITIONS }, + }, +}; diff --git a/superset-frontend/src/components/Loading/Loading.test.tsx b/superset-frontend/src/components/Loading/Loading.test.tsx new file mode 100644 index 00000000000..243603fda6e --- /dev/null +++ b/superset-frontend/src/components/Loading/Loading.test.tsx @@ -0,0 +1,65 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React from 'react'; +import '@testing-library/jest-dom'; +import { render, screen } from '@testing-library/react'; +import Loading from './index'; + +test('Rerendering correctly with default props', () => { + render(); + const loading = screen.getByRole('status'); + const classNames = loading.getAttribute('class')?.split(' '); + const imagePath = loading.getAttribute('src'); + const ariaLive = loading.getAttribute('aria-live'); + const ariaLabel = loading.getAttribute('aria-label'); + expect(loading).toBeInTheDocument(); + expect(imagePath).toBe('/static/assets/images/loading.gif'); + expect(classNames).toContain('floating'); + expect(classNames).toContain('loading'); + expect(ariaLive).toContain('polite'); + expect(ariaLabel).toContain('Loading'); +}); + +test('Position must be a class', () => { + render(); + const loading = screen.getByRole('status'); + const classNames = loading.getAttribute('class')?.split(' '); + expect(loading).toBeInTheDocument(); + expect(classNames).not.toContain('floating'); + expect(classNames).toContain('normal'); +}); + +test('support for extra classes', () => { + render(); + const loading = screen.getByRole('status'); + const classNames = loading.getAttribute('class')?.split(' '); + expect(loading).toBeInTheDocument(); + expect(classNames).toContain('loading'); + expect(classNames).toContain('floating'); + expect(classNames).toContain('extra-class'); +}); + +test('Diferent image path', () => { + render(); + const loading = screen.getByRole('status'); + const imagePath = loading.getAttribute('src'); + expect(loading).toBeInTheDocument(); + expect(imagePath).toBe('/images/loading.gif'); +}); diff --git a/superset-frontend/src/components/Loading.tsx b/superset-frontend/src/components/Loading/index.tsx similarity index 72% rename from superset-frontend/src/components/Loading.tsx rename to superset-frontend/src/components/Loading/index.tsx index 4eb92cb1f15..d6253560287 100644 --- a/superset-frontend/src/components/Loading.tsx +++ b/superset-frontend/src/components/Loading/index.tsx @@ -16,11 +16,16 @@ * specific language governing permissions and limitations * under the License. */ + import React from 'react'; import { styled } from '@superset-ui/core'; +import cls from 'classnames'; -interface Props { - position?: string; +export type PositionOption = 'normal' | 'inline' | 'floating'; +export interface Props { + position?: PositionOption; + className?: string; + image?: string; } const LoaderImg = styled.img` @@ -41,12 +46,19 @@ const LoaderImg = styled.img` transform: translate(-50%, -50%); } `; -export default function Loading({ position = 'floating' }: Props) { +export default function Loading({ + position = 'floating', + image = '/static/assets/images/loading.gif', + className, +}: Props) { return ( ); }