mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-02 03:25:56 +01:00
Integrate tailwind css and create new button library (#2098)
Introduce tailwind as new frontend styling library to replace bulma in the longer run. Also create the first new ui library `ui-buttons` which will be the new standard for buttons ins SCM-Manager. In this library we reconsidered which types of buttons should be used to create a clean and consistent ui. Co-authored-by: Eduard Heimbuch <eduard.heimbuch@cloudogu.com>
This commit is contained in:
committed by
GitHub
parent
09beb8cd3b
commit
27dbcbf28d
4
scm-ui/ui-buttons/.storybook/.babelrc
Normal file
4
scm-ui/ui-buttons/.storybook/.babelrc
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"presets": ["@scm-manager/babel-preset"],
|
||||
"plugins": ["@babel/plugin-syntax-dynamic-import"]
|
||||
}
|
||||
57
scm-ui/ui-buttons/.storybook/RemoveThemesPlugin.js
Normal file
57
scm-ui/ui-buttons/.storybook/RemoveThemesPlugin.js
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
|
||||
class RemoveThemesPlugin {
|
||||
apply (compiler) {
|
||||
compiler.hooks.compilation.tap('RemoveThemesPlugin', (compilation) => {
|
||||
|
||||
HtmlWebpackPlugin.getHooks(compilation).beforeAssetTagGeneration.tapAsync(
|
||||
'RemoveThemesPlugin',
|
||||
(data, cb) => {
|
||||
|
||||
// remove generated style-loader bundles from the page
|
||||
// there should be a better way, which does not generate the bundles at all
|
||||
// but for now it works
|
||||
if (data.assets.js) {
|
||||
data.assets.js = data.assets.js.filter(bundle => !bundle.startsWith("ui-theme-"))
|
||||
.filter(bundle => !bundle.startsWith("runtime~ui-theme-"))
|
||||
}
|
||||
|
||||
// remove css links to avoid conflicts with the themes
|
||||
// so we remove all and add our own via preview-head.html
|
||||
if (data.assets.css) {
|
||||
data.assets.css = data.assets.css.filter(css => !css.startsWith("ui-theme-"))
|
||||
}
|
||||
|
||||
// Tell webpack to move on
|
||||
cb(null, data)
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = RemoveThemesPlugin
|
||||
27
scm-ui/ui-buttons/.storybook/index.css
Normal file
27
scm-ui/ui-buttons/.storybook/index.css
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
109
scm-ui/ui-buttons/.storybook/main.js
Normal file
109
scm-ui/ui-buttons/.storybook/main.js
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
|
||||
const RemoveThemesPlugin = require("./RemoveThemesPlugin");
|
||||
const ReactDOM = require("react-dom");
|
||||
|
||||
const root = path.resolve("..");
|
||||
|
||||
const themedir = path.join(root, "ui-styles", "src");
|
||||
|
||||
ReactDOM.createPortal = (node) => node;
|
||||
|
||||
const themes = fs
|
||||
.readdirSync(themedir)
|
||||
.map((filename) => path.parse(filename))
|
||||
.filter((p) => p.ext === ".scss")
|
||||
.reduce((entries, current) => ({ ...entries, [`ui-theme-${current.name}`]: path.join(themedir, current.base) }), {});
|
||||
|
||||
module.exports = {
|
||||
typescript: { reactDocgen: false },
|
||||
core: {
|
||||
builder: "webpack5",
|
||||
},
|
||||
stories: ["../docs/**/*.stories.mdx", "../src/**/*.stories.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"],
|
||||
addons: [
|
||||
"storybook-addon-i18next",
|
||||
"storybook-addon-themes",
|
||||
"@storybook/addon-links",
|
||||
"@storybook/addon-essentials",
|
||||
"@storybook/addon-interactions",
|
||||
"@storybook/addon-a11y",
|
||||
"storybook-addon-pseudo-states"
|
||||
],
|
||||
framework: "@storybook/react",
|
||||
webpackFinal: async (config) => {
|
||||
// add our themes to webpack entry points
|
||||
config.entry = {
|
||||
main: config.entry,
|
||||
...themes,
|
||||
};
|
||||
|
||||
// create separate css files for our themes
|
||||
config.plugins.push(
|
||||
new MiniCssExtractPlugin({
|
||||
filename: "[name].css",
|
||||
ignoreOrder: false,
|
||||
})
|
||||
);
|
||||
|
||||
config.module.rules.push({
|
||||
test: /\.scss$/,
|
||||
use: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"],
|
||||
});
|
||||
|
||||
config.module.rules.push({
|
||||
test: /\.css$/,
|
||||
use: [
|
||||
{
|
||||
loader: "postcss-loader",
|
||||
options: {
|
||||
postcssOptions: {
|
||||
plugins: {
|
||||
tailwindcss: { config: require("./tailwind.config") },
|
||||
autoprefixer: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
include: path.resolve(__dirname, "../"),
|
||||
});
|
||||
|
||||
// the html-webpack-plugin adds the generated css and js files to the iframe,
|
||||
// which overrides our manually loaded css files.
|
||||
// So we use a custom plugin which uses a hook of html-webpack-plugin
|
||||
// to filter our themes from the output.
|
||||
config.plugins.push(new RemoveThemesPlugin());
|
||||
|
||||
// force cjs instead of esm
|
||||
// https://github.com/tannerlinsley/react-query/issues/3513
|
||||
config.resolve.alias["react-query/devtools"] = require.resolve("react-query/devtools");
|
||||
|
||||
return config;
|
||||
},
|
||||
};
|
||||
26
scm-ui/ui-buttons/.storybook/preview-head.html
Normal file
26
scm-ui/ui-buttons/.storybook/preview-head.html
Normal file
@@ -0,0 +1,26 @@
|
||||
<!--
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
-->
|
||||
|
||||
<link id="ui-theme" data-theme="light" rel="stylesheet" type="text/css" href="/ui-theme-light.css">
|
||||
|
||||
97
scm-ui/ui-buttons/.storybook/preview.js
Normal file
97
scm-ui/ui-buttons/.storybook/preview.js
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import "./index.css";
|
||||
|
||||
import i18next from "i18next";
|
||||
import { initReactI18next } from "react-i18next";
|
||||
import { withI18next } from "storybook-addon-i18next";
|
||||
import React, {useEffect} from "react";
|
||||
import withApiProvider from "./withApiProvider";
|
||||
import { withThemes } from 'storybook-addon-themes/react';
|
||||
|
||||
let i18n = i18next;
|
||||
|
||||
// only use fetch backend for storybook
|
||||
// and not for storyshots
|
||||
if (!process.env.JEST_WORKER_ID) {
|
||||
const Backend = require("i18next-fetch-backend");
|
||||
i18n = i18n.use(Backend.default);
|
||||
}
|
||||
|
||||
i18n.use(initReactI18next).init({
|
||||
whitelist: ["en", "de", "es"],
|
||||
lng: "en",
|
||||
fallbackLng: "en",
|
||||
interpolation: {
|
||||
escapeValue: false,
|
||||
},
|
||||
react: {
|
||||
useSuspense: false,
|
||||
},
|
||||
backend: {
|
||||
loadPath: "/locales/{{lng}}/{{ns}}.json",
|
||||
init: {
|
||||
credentials: "same-origin",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const decorators = [
|
||||
withI18next({
|
||||
i18n,
|
||||
languages: {
|
||||
en: "English",
|
||||
de: "Deutsch",
|
||||
es: "Spanisch",
|
||||
},
|
||||
}),
|
||||
withApiProvider,
|
||||
withThemes
|
||||
];
|
||||
|
||||
const Decorator = ({children, themeName}) => {
|
||||
useEffect(() => {
|
||||
const link = document.querySelector("#ui-theme");
|
||||
if (link && link["data-theme"] !== themeName) {
|
||||
link.href = `ui-theme-${themeName}.css`;
|
||||
link["data-theme"] = themeName;
|
||||
}
|
||||
}, [themeName]);
|
||||
return <>{children}</>
|
||||
};
|
||||
|
||||
export const parameters = {
|
||||
actions: { argTypesRegex: "^on[A-Z].*" },
|
||||
themes: {
|
||||
Decorator,
|
||||
clearable: false,
|
||||
default: "light",
|
||||
list: [
|
||||
{ name: "light", color: "#fff" },
|
||||
{ name: "highcontrast", color: "#050514" },
|
||||
{ name: "dark", color: "#121212" },
|
||||
],
|
||||
}
|
||||
};
|
||||
33
scm-ui/ui-buttons/.storybook/tailwind.config.js
Normal file
33
scm-ui/ui-buttons/.storybook/tailwind.config.js
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
const path = require("path");
|
||||
|
||||
module.exports = {
|
||||
presets: [
|
||||
// eslint-disable-next-line global-require,import/no-extraneous-dependencies
|
||||
require("@scm-manager/ui-scripts/src/tailwind.config"),
|
||||
],
|
||||
content: [path.join(__dirname, "../{src,docs}/**/*.{tsx,mdx}")],
|
||||
};
|
||||
45
scm-ui/ui-buttons/.storybook/withApiProvider.js
Normal file
45
scm-ui/ui-buttons/.storybook/withApiProvider.js
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
import * as React from "react";
|
||||
import { ApiProvider } from "@scm-manager/ui-api";
|
||||
|
||||
const withApiProvider = (storyFn) => {
|
||||
return React.createElement(ApiProvider, {
|
||||
index: {
|
||||
version: "x.y.z",
|
||||
_links: {}
|
||||
},
|
||||
me: {
|
||||
name: "trillian",
|
||||
displayName: "Trillian McMillan",
|
||||
mail: "trillian@hitchhiker.com",
|
||||
groups: [],
|
||||
_links: {}
|
||||
},
|
||||
devtools: false,
|
||||
children: storyFn()
|
||||
});
|
||||
}
|
||||
|
||||
export default withApiProvider;
|
||||
64
scm-ui/ui-buttons/docs/introduction.stories.mdx
Normal file
64
scm-ui/ui-buttons/docs/introduction.stories.mdx
Normal file
@@ -0,0 +1,64 @@
|
||||
import { Meta } from "@storybook/addon-docs";
|
||||
import { Button } from "../src";
|
||||
|
||||
<Meta title="Introduction"/>
|
||||
|
||||
# Buttons
|
||||
|
||||
The `@scm-manager/ui-buttons` library provides [atoms](https://atomicdesign.bradfrost.com/chapter-2/#atoms) implemented
|
||||
as minimal wrappers around native html elements styled to match the general SCM-Manager aesthetic.
|
||||
|
||||
## Components
|
||||
|
||||
There are three actionable components available. Styling is consistent amongst them and all have the required `variant` property.
|
||||
|
||||
1. [Button](?path=/story/components--button)
|
||||
2. [Link Button](?path=/story/components--link-button)
|
||||
3. [External Link Button](?path=/story/components--external-link-button)
|
||||
|
||||
## Usage
|
||||
|
||||
Actionable components serve a dedicated purpose. It is therefore important to know when and how to use them.
|
||||
|
||||
### Variants
|
||||
|
||||
There are four variants available to each of the three button types, varying in importance.
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Emphasis</th>
|
||||
<th>Button Variant</th>
|
||||
<th>Usage Examples</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Very High</td>
|
||||
<td><Button variant="signal" className="w-full">Signal</Button></td>
|
||||
<td>Destructive actions</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>High</td>
|
||||
<td><Button variant="primary" className="w-full">Primary</Button></td>
|
||||
<td>Form submit</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Normal</td>
|
||||
<td><Button variant="secondary" className="w-full">Secondary</Button></td>
|
||||
<td>Cancel action in dialog</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Low</td>
|
||||
<td><Button variant="tertiary" className="w-full">Tertiary</Button></td>
|
||||
<td>Circumstantially relevant action on page with many actionable elements</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
## Content
|
||||
|
||||
Buttons exclusively contain text and no icons. Icons tend to be ambiguous and not always applicable which leads to inconsistent
|
||||
and cluttered layouts.
|
||||
|
||||
Button text should be short, concise and describe the action performed.
|
||||
22
scm-ui/ui-buttons/docs/usage.stories.mdx
Normal file
22
scm-ui/ui-buttons/docs/usage.stories.mdx
Normal file
@@ -0,0 +1,22 @@
|
||||
import { Meta, Story } from "@storybook/addon-docs";
|
||||
import { Button } from "../src";
|
||||
|
||||
<Meta title="Usage" parameters={{
|
||||
storyshots: { disable: true }
|
||||
}} />
|
||||
|
||||
In confirmation dialogs, there are two actions.<br/>
|
||||
One to cancel the current process and one to confirm it.<br/>
|
||||
Aborting is always the secondary action, confirmation always the primary.
|
||||
Focus is always on the cancelling action.
|
||||
|
||||
<Story name="Confirmation Dialog">
|
||||
<div className="max-w-2xl rounded border p-4">
|
||||
<h4 className="mb-2 font-bold">Delete User</h4>
|
||||
<p>Do you really want to delete this user ?</p>
|
||||
<div className="flex justify-end gap-2">
|
||||
<Button variant="secondary">Cancel</Button>
|
||||
<Button variant="primary">Delete</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Story>
|
||||
85
scm-ui/ui-buttons/package.json
Normal file
85
scm-ui/ui-buttons/package.json
Normal file
@@ -0,0 +1,85 @@
|
||||
{
|
||||
"name": "@scm-manager/ui-buttons",
|
||||
"version": "2.37.3-SNAPSHOT",
|
||||
"private": true,
|
||||
"main": "build/index.js",
|
||||
"module": "build/index.mjs",
|
||||
"types": "build/index.d.ts",
|
||||
"files": [
|
||||
"build"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "tsup ./src/index.ts -d build --format esm,cjs --dts",
|
||||
"dev": "tsup ./src/index.ts -d build --format esm,cjs --dts --watch",
|
||||
"lint": "eslint src",
|
||||
"typecheck": "tsc",
|
||||
"storybook": "start-storybook -p 6006 -s ../ui-webapp/public",
|
||||
"build-storybook": "build-storybook",
|
||||
"image-snapshots": "jest \"image-snapshot.test.ts\"",
|
||||
"a11y-check": "jest \"a11y.test.ts\"",
|
||||
"depcheck": "depcheck --ignores=@scm-manager/prettier-config,@scm-manager/tsconfig,@babel/core,sass-loader,autoprefixer,babel-loader,postcss-loader,tailwindcss,storybook-addon-*,@storybook/*,webpack"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^17.0.1",
|
||||
"react-dom": "^17.0.1",
|
||||
"react-router-dom": "^5.3.1",
|
||||
"classnames": "^2.2.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@scm-manager/ui-scripts": "2.37.3-SNAPSHOT",
|
||||
"@scm-manager/ui-styles": "2.37.3-SNAPSHOT",
|
||||
"@scm-manager/ui-api": "2.37.3-SNAPSHOT",
|
||||
"@scm-manager/eslint-config": "^2.16.0",
|
||||
"@babel/core": "^7.17.8",
|
||||
"@scm-manager/tsconfig": "^2.12.0",
|
||||
"@storybook/addon-essentials": "^6.4.20",
|
||||
"@storybook/addon-interactions": "^6.4.20",
|
||||
"@storybook/addon-a11y": "^6.4.20",
|
||||
"@storybook/addon-links": "^6.4.20",
|
||||
"@storybook/builder-webpack5": "^6.4.20",
|
||||
"@storybook/manager-webpack5": "^6.4.20",
|
||||
"@storybook/react": "^6.4.20",
|
||||
"@storybook/addon-storyshots-puppeteer": "^6.4.20",
|
||||
"@storybook/addon-storyshots": "^6.4.20",
|
||||
"@storybook/testing-library": "^0.0.9",
|
||||
"jest-transform-css": "^4.0.1",
|
||||
"puppeteer": "^15.5.0",
|
||||
"storybook-addon-pseudo-states": "^1.15.1",
|
||||
"storybook-react-router": "^1.0.8",
|
||||
"@types/storybook-react-router": "^1.0.2",
|
||||
"sass-loader": "^12.3.0",
|
||||
"storybook-addon-themes": "^6.1.0",
|
||||
"autoprefixer": "^10.4.4",
|
||||
"babel-loader": "^8.2.4",
|
||||
"postcss": "^8.4.12",
|
||||
"postcss-loader": "^6.2.1",
|
||||
"tailwindcss": "^3.0.23",
|
||||
"webpack": "5",
|
||||
"tsup": "^6.1.2",
|
||||
"mini-css-extract-plugin": "^1.6.2",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"react-query": "^3.25.1",
|
||||
"i18next": "^19.9.2",
|
||||
"react-i18next": "^10.13.2",
|
||||
"i18next-fetch-backend": "^2.3.1"
|
||||
},
|
||||
"babel": {
|
||||
"presets": [
|
||||
"@scm-manager/babel-preset"
|
||||
]
|
||||
},
|
||||
"jest": {
|
||||
"transform": {
|
||||
"^.+\\.[tj]sx?$": "babel-jest",
|
||||
"^.+\\.(css|less|scss)$": "jest-transform-css",
|
||||
"^.+\\.mdx?$": "@storybook/addon-docs/jest-transform-mdx"
|
||||
}
|
||||
},
|
||||
"prettier": "@scm-manager/prettier-config",
|
||||
"eslintConfig": {
|
||||
"extends": "@scm-manager/eslint-config"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "restricted"
|
||||
}
|
||||
}
|
||||
30
scm-ui/ui-buttons/postcss.config.js
Normal file
30
scm-ui/ui-buttons/postcss.config.js
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 35 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
34
scm-ui/ui-buttons/src/a11y.test.ts
Normal file
34
scm-ui/ui-buttons/src/a11y.test.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
import initStoryshots from "@storybook/addon-storyshots";
|
||||
import { axeTest } from "@storybook/addon-storyshots-puppeteer";
|
||||
import path from "path";
|
||||
|
||||
initStoryshots({
|
||||
suite: "A11y checks",
|
||||
test: axeTest({
|
||||
storybookUrl: `file://${path.resolve(__dirname, "../storybook-static")}`,
|
||||
}),
|
||||
storyNameRegex: /High-Contrast States/,
|
||||
});
|
||||
91
scm-ui/ui-buttons/src/button.stories.tsx
Normal file
91
scm-ui/ui-buttons/src/button.stories.tsx
Normal file
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
|
||||
import {
|
||||
Button as ButtonComponent,
|
||||
ButtonVariantList,
|
||||
ButtonVariants,
|
||||
ExternalLinkButton as ExternalLinkButtonComponent,
|
||||
LinkButton as LinkButtonComponent,
|
||||
} from "./button";
|
||||
import StoryRouter from "storybook-react-router";
|
||||
import { StoryFn } from "@storybook/react";
|
||||
|
||||
type ExtractProps<T> = T extends React.ComponentType<infer U> ? U : never;
|
||||
|
||||
// More on default export: https://storybook.js.org/docs/react/writing-stories/introduction#default-export
|
||||
export default {
|
||||
title: "Components",
|
||||
component: null,
|
||||
subcomponents: {
|
||||
Button: ButtonComponent,
|
||||
LinkButton: LinkButtonComponent,
|
||||
ExternalLinkButton: ExternalLinkButtonComponent,
|
||||
},
|
||||
argTypes: {
|
||||
variant: {
|
||||
options: ButtonVariantList,
|
||||
control: { type: "select" },
|
||||
},
|
||||
},
|
||||
decorators: [StoryRouter()],
|
||||
parameters: {
|
||||
storyshots: { disable: true },
|
||||
},
|
||||
};
|
||||
|
||||
// More on component templates: https://storybook.js.org/docs/react/writing-stories/introduction#using-args
|
||||
const ButtonTemplate: StoryFn<ExtractProps<typeof ButtonComponent>> = (args) => <ButtonComponent {...args} />;
|
||||
const LinkButtonTemplate: StoryFn<ExtractProps<typeof LinkButtonComponent>> = (args) => (
|
||||
<LinkButtonComponent {...args} />
|
||||
);
|
||||
const ExternalLinkButtonTemplate: StoryFn<ExtractProps<typeof ExternalLinkButtonComponent>> = (args) => (
|
||||
<ExternalLinkButtonComponent {...args} />
|
||||
);
|
||||
|
||||
export const Button = ButtonTemplate.bind({});
|
||||
// More on args: https://storybook.js.org/docs/react/writing-stories/args
|
||||
Button.args = {
|
||||
children: "Button",
|
||||
variant: ButtonVariants.PRIMARY,
|
||||
disabled: false,
|
||||
};
|
||||
|
||||
export const LinkButton = LinkButtonTemplate.bind({});
|
||||
// More on args: https://storybook.js.org/docs/react/writing-stories/args
|
||||
LinkButton.args = {
|
||||
children: "Link Button",
|
||||
to: "/repos",
|
||||
variant: ButtonVariants.PRIMARY,
|
||||
};
|
||||
|
||||
export const ExternalLinkButton = ExternalLinkButtonTemplate.bind({});
|
||||
// More on args: https://storybook.js.org/docs/react/writing-stories/args
|
||||
ExternalLinkButton.args = {
|
||||
children: "External Link Button",
|
||||
href: "https://scm-manager.org",
|
||||
variant: ButtonVariants.PRIMARY,
|
||||
};
|
||||
73
scm-ui/ui-buttons/src/button.test.stories.mdx
Normal file
73
scm-ui/ui-buttons/src/button.test.stories.mdx
Normal file
@@ -0,0 +1,73 @@
|
||||
import { Meta, Story } from "@storybook/addon-docs";
|
||||
import { Button, ButtonVariantList } from "./button";
|
||||
|
||||
<Meta title="Tests"/>
|
||||
|
||||
<Story name="Light States" parameters={{
|
||||
pseudo: {
|
||||
hover: ButtonVariantList.map(variant => `#${variant}-Hover`),
|
||||
focus: ButtonVariantList.map(variant => `#${variant}-Focus`),
|
||||
active: ButtonVariantList.map(variant => `#${variant}-Active`),
|
||||
},
|
||||
themes: {
|
||||
default: 'light',
|
||||
},
|
||||
}}>
|
||||
<table className="border-separate border-spacing-4">
|
||||
<tr>
|
||||
<th>STATE</th>
|
||||
{ButtonVariantList.map(variant => <th>{variant.toUpperCase()}</th>)}
|
||||
</tr>
|
||||
{["Normal", "Hover", "Active", "Focus", "Disabled"].map(state => <tr>
|
||||
<td>{state}</td>
|
||||
{ButtonVariantList.map(variant => <td><Button id={`${variant}-${state}`} disabled={state === "Disabled"}
|
||||
variant={variant}>Button</Button></td>)}
|
||||
</tr>)}
|
||||
</table>
|
||||
</Story>
|
||||
|
||||
<Story name="Dark States" parameters={{
|
||||
pseudo: {
|
||||
hover: ButtonVariantList.map(variant => `#${variant}-Hover`),
|
||||
focus: ButtonVariantList.map(variant => `#${variant}-Focus`),
|
||||
active: ButtonVariantList.map(variant => `#${variant}-Active`),
|
||||
},
|
||||
themes: {
|
||||
default: 'dark',
|
||||
},
|
||||
}}>
|
||||
<table className="border-separate border-spacing-4">
|
||||
<tr>
|
||||
<th>STATE</th>
|
||||
{ButtonVariantList.map(variant => <th>{variant.toUpperCase()}</th>)}
|
||||
</tr>
|
||||
{["Normal", "Hover", "Active", "Focus", "Disabled"].map(state => <tr>
|
||||
<td>{state}</td>
|
||||
{ButtonVariantList.map(variant => <td><Button id={`${variant}-${state}`} disabled={state === "Disabled"}
|
||||
variant={variant}>Button</Button></td>)}
|
||||
</tr>)}
|
||||
</table>
|
||||
</Story>
|
||||
|
||||
<Story name="High-Contrast States" parameters={{
|
||||
pseudo: {
|
||||
hover: ButtonVariantList.map(variant => `#${variant}-Hover`),
|
||||
focus: ButtonVariantList.map(variant => `#${variant}-Focus`),
|
||||
active: ButtonVariantList.map(variant => `#${variant}-Active`),
|
||||
},
|
||||
themes: {
|
||||
default: 'highcontrast',
|
||||
},
|
||||
}}>
|
||||
<table className="border-separate border-spacing-4">
|
||||
<tr>
|
||||
<th>STATE</th>
|
||||
{ButtonVariantList.map(variant => <th>{variant.toUpperCase()}</th>)}
|
||||
</tr>
|
||||
{["Normal", "Hover", "Active", "Focus", "Disabled"].map(state => <tr>
|
||||
<td>{state}</td>
|
||||
{ButtonVariantList.map(variant => <td><Button id={`${variant}-${state}`} disabled={state === "Disabled"}
|
||||
variant={variant}>Button</Button></td>)}
|
||||
</tr>)}
|
||||
</table>
|
||||
</Story>
|
||||
109
scm-ui/ui-buttons/src/button.tsx
Normal file
109
scm-ui/ui-buttons/src/button.tsx
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import React, { AnchorHTMLAttributes, ButtonHTMLAttributes } from "react";
|
||||
import { Link as ReactRouterLink, LinkProps as ReactRouterLinkProps } from "react-router-dom";
|
||||
import classNames from "classnames";
|
||||
|
||||
export const ButtonVariants = {
|
||||
PRIMARY: "primary",
|
||||
SECONDARY: "secondary",
|
||||
TERTIARY: "tertiary",
|
||||
SIGNAL: "signal",
|
||||
} as const;
|
||||
|
||||
export const ButtonVariantList = Object.values(ButtonVariants);
|
||||
|
||||
type ButtonVariant = typeof ButtonVariants[keyof typeof ButtonVariants];
|
||||
|
||||
const BASE_BUTTON_CLASSES = classNames(
|
||||
"inline-block whitespace-nowrap rounded border py-2 px-6 text-center font-semibold focus:z-10 focus:outline focus:outline-offset-2 focus:outline-purple-500 disabled:cursor-not-allowed"
|
||||
);
|
||||
const DEFAULT_BUTTON_CLASSES = classNames(
|
||||
"border-gray-200 hover:border-gray-400 active:shadow-inner disabled:hover:border-gray-200 disabled:active:shadow-none"
|
||||
);
|
||||
const PRIMARY_BUTTON_CLASSES = classNames(
|
||||
"border-transparent bg-primary text-primary-contrast hover:bg-primary-hover active:bg-primary-active disabled:bg-primary-disabled disabled:text-primary-disabled-contrast "
|
||||
);
|
||||
const SECONDARY_BUTTON_CLASSES = classNames(
|
||||
"border-primary text-primary hover:border-primary-hover hover:text-primary-hover active:border-primary-active active:text-primary-active disabled:border-primary-disabled disabled:text-primary-disabled"
|
||||
);
|
||||
const TERTIARY_BUTTON_CLASSES = classNames(
|
||||
"border-transparent text-primary hover:text-primary-hover active:text-primary-active disabled:text-primary-disabled"
|
||||
);
|
||||
const SIGNAL_BUTTON_CLASSES = classNames(
|
||||
"border-transparent bg-signal text-signal-contrast hover:bg-signal-hover hover:text-signal-hover-contrast active:bg-signal-active active:text-signal-active-contrast disabled:bg-signal-disabled disabled:text-signal-disabled-contrast"
|
||||
);
|
||||
|
||||
const createButtonClasses = (variant?: ButtonVariant) =>
|
||||
classNames(BASE_BUTTON_CLASSES, {
|
||||
[DEFAULT_BUTTON_CLASSES]: !variant,
|
||||
[PRIMARY_BUTTON_CLASSES]: variant === "primary",
|
||||
[SECONDARY_BUTTON_CLASSES]: variant === "secondary",
|
||||
[TERTIARY_BUTTON_CLASSES]: variant === "tertiary",
|
||||
[SIGNAL_BUTTON_CLASSES]: variant === "signal",
|
||||
});
|
||||
|
||||
type BaseButtonProps = {
|
||||
variant: ButtonVariant;
|
||||
};
|
||||
|
||||
type ButtonProps = BaseButtonProps & ButtonHTMLAttributes<HTMLButtonElement>;
|
||||
|
||||
/**
|
||||
* Styled html button
|
||||
*/
|
||||
export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
({ className, variant, children, ...props }, ref) => (
|
||||
<button {...props} className={classNames(createButtonClasses(variant), className)} ref={ref}>
|
||||
{children}
|
||||
</button>
|
||||
)
|
||||
);
|
||||
|
||||
type LinkButtonProps = BaseButtonProps & ReactRouterLinkProps;
|
||||
|
||||
/**
|
||||
* Styled react router link
|
||||
*/
|
||||
export const LinkButton = React.forwardRef<HTMLAnchorElement, LinkButtonProps>(
|
||||
({ className, variant, children, ...props }, ref) => (
|
||||
<ReactRouterLink {...props} className={classNames(createButtonClasses(variant), className)} ref={ref}>
|
||||
{children}
|
||||
</ReactRouterLink>
|
||||
)
|
||||
);
|
||||
|
||||
type ExternalLinkButtonProps = BaseButtonProps & AnchorHTMLAttributes<HTMLAnchorElement>;
|
||||
|
||||
/**
|
||||
* Styled html anchor
|
||||
*/
|
||||
export const ExternalLinkButton = React.forwardRef<HTMLAnchorElement, ExternalLinkButtonProps>(
|
||||
({ className, variant, children, ...props }, ref) => (
|
||||
<a {...props} className={classNames(createButtonClasses(variant), className)} ref={ref}>
|
||||
{children}
|
||||
</a>
|
||||
)
|
||||
);
|
||||
33
scm-ui/ui-buttons/src/image-snapshot.test.ts
Normal file
33
scm-ui/ui-buttons/src/image-snapshot.test.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
import path from "path";
|
||||
import initStoryshots from "@storybook/addon-storyshots";
|
||||
import { imageSnapshot } from "@storybook/addon-storyshots-puppeteer";
|
||||
|
||||
initStoryshots({
|
||||
suite: "Image snapshots",
|
||||
test: imageSnapshot({
|
||||
storybookUrl: `file://${path.resolve(__dirname, "../storybook-static")}`,
|
||||
})
|
||||
});
|
||||
26
scm-ui/ui-buttons/src/index.css
Normal file
26
scm-ui/ui-buttons/src/index.css
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
27
scm-ui/ui-buttons/src/index.ts
Normal file
27
scm-ui/ui-buttons/src/index.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import "./index.css";
|
||||
|
||||
export { Button, LinkButton, ExternalLinkButton, ButtonVariants } from "./button";
|
||||
34
scm-ui/ui-buttons/tailwind.config.js
Normal file
34
scm-ui/ui-buttons/tailwind.config.js
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
const path = require("path");
|
||||
|
||||
module.exports = {
|
||||
presets: [
|
||||
// eslint-disable-next-line global-require,import/no-extraneous-dependencies
|
||||
require("@scm-manager/ui-styles/src/tailwind.config.preset"),
|
||||
],
|
||||
content: [path.join(__dirname, "src/**/*.tsx")],
|
||||
important: true,
|
||||
};
|
||||
7
scm-ui/ui-buttons/tsconfig.json
Normal file
7
scm-ui/ui-buttons/tsconfig.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "@scm-manager/tsconfig",
|
||||
"include": [
|
||||
"./src",
|
||||
"./docs"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user