diff --git a/components/button/src/button/button.js b/components/button/src/button/button.js
index f3313500ea..bbfa6065a1 100644
--- a/components/button/src/button/button.js
+++ b/components/button/src/button/button.js
@@ -1,5 +1,6 @@
import { CircularLoader } from '@dhis2-ui/loader'
import { sharedPropTypes } from '@dhis2/ui-constants'
+import { NativeButton } from '../index.js'
import cx from 'classnames'
import PropTypes from 'prop-types'
import React, { useEffect, useRef } from 'react'
@@ -55,7 +56,7 @@ export const Button = ({
})
return (
-
+
)
}
diff --git a/components/button/src/button/button.styles.js b/components/button/src/button/button.styles.js
index e21b631a83..9a5c08e039 100644
--- a/components/button/src/button/button.styles.js
+++ b/components/button/src/button/button.styles.js
@@ -2,77 +2,6 @@ import { colors, theme, spacers } from '@dhis2/ui-constants'
import css from 'styled-jsx/css'
export default css`
- button {
- display: inline-flex;
- position: relative;
- align-items: center;
- justify-content: center;
- border-radius: 4px;
- font-weight: 400;
- letter-spacing: 0.5px;
- text-decoration: none;
- cursor: pointer;
- user-select: none;
- color: ${colors.grey900};
-
- /*medium*/
- height: 36px;
- padding: 0 ${spacers.dp12};
- font-size: 14px;
- line-height: 16px;
-
- /*basic*/
- border: 1px solid ${colors.grey500};
- background-color: #f9fafb;
- }
-
- button:disabled {
- cursor: not-allowed;
- }
-
- button:focus {
- outline: 3px solid ${theme.focus};
- outline-offset: -3px;
- text-decoration: underline;
- }
-
- /* Prevent focus styles when mouse clicking */
- button:focus:not(:focus-visible) {
- outline: none;
- text-decoration: none;
- }
-
- /* Prevent focus styles on active and disabled buttons */
- button:active:focus,
- button:disabled:focus {
- outline: none;
- text-decoration: none;
- }
-
- button:hover {
- border-color: ${colors.grey500};
- background-color: ${colors.grey200};
- }
-
- button:active,
- button:active:focus {
- border-color: ${colors.grey500};
- background-color: ${colors.grey200};
- box-shadow: 0 0 0 1px rgb(0, 0, 0, 0.1) inset;
- }
-
- button:focus {
- background-color: #f9fafb;
- }
-
- button:disabled {
- border-color: ${colors.grey400};
- background-color: #f9fafb;
- box-shadow: none;
- color: ${theme.disabled};
- fill: ${theme.disabled};
- }
-
.small {
height: 28px;
padding: 0 6px;
diff --git a/components/button/src/native-button/button.stories.js b/components/button/src/native-button/button.stories.js
new file mode 100644
index 0000000000..904613a0aa
--- /dev/null
+++ b/components/button/src/native-button/button.stories.js
@@ -0,0 +1,290 @@
+import { sharedPropTypes } from '@dhis2/ui-constants'
+import React from 'react'
+import { Button } from './button.js'
+
+// Note: make sure 'fenced code blocks' are not indentend in this template string
+const description = `Buttons are used for triggering actions.
+There are different types of buttons in the design system which are intended
+for different types of actions.
+
+\`\`\`js
+import { Button } from '@dhis2/ui'
+\`\`\``
+
+const { buttonVariantArgType, sizeArgType } = sharedPropTypes
+
+const logger = ({ name, value }) => console.log(`${name}: ${value}`)
+
+export default {
+ title: 'Button',
+ component: Button,
+ parameters: {
+ componentSubtitle: 'Initiates an action',
+ docs: {
+ description: {
+ component: description,
+ },
+ },
+ },
+ args: {
+ children: 'Label me!',
+ value: 'default',
+ onClick: logger,
+ },
+ argTypes: {
+ primary: { ...buttonVariantArgType },
+ secondary: { ...buttonVariantArgType },
+ destructive: { ...buttonVariantArgType },
+ small: { ...sizeArgType },
+ large: { ...sizeArgType },
+ },
+}
+
+const DemoIcon24 = (
+
+)
+
+const DemoIcon16 = (
+
+)
+
+const Template = (args) =>
+
+export const Basic = Template.bind({})
+Basic.args = {
+ name: 'Basic button',
+}
+
+export const Primary = Template.bind({})
+Primary.args = {
+ primary: true,
+ name: 'Primary button',
+}
+Primary.parameters = {
+ docs: {
+ description: {
+ story: 'Used to highlight the most important/main action on a page. \
+ A "Save" button for a form page should be primary, for example. \
+ Use sparingly, rarely should there be more than a single primary \
+ button per page.',
+ },
+ },
+}
+
+export const Secondary = Template.bind({})
+Secondary.args = {
+ secondary: true,
+ name: 'Secondary button',
+}
+Secondary.parameters = {
+ docs: {
+ description: {
+ story: 'Used for passive actions, often as an alternative to the primary \
+ action. If "Save" is primary, "Cancel" could be secondary. \
+ Not intended to draw user attention. Do not use for the only \
+ action on a page.',
+ },
+ },
+}
+
+export const Destructive = Template.bind({})
+Destructive.args = {
+ destructive: true,
+ name: 'Destructive button',
+}
+Destructive.parameters = {
+ docs: {
+ description: {
+ story: 'Used instead of a primary button when the main action is \
+ destructive in nature. Used to highlight to the user the \
+ seriousness of the action. \
+ **Destructive buttons must only be used for destructive actions.**',
+ },
+ },
+}
+export const DestructiveSecondary = Template.bind({})
+DestructiveSecondary.args = {
+ destructive: true,
+ secondary: true,
+ name: 'Destructive secondary button',
+}
+
+export const Disabled = (args) => (
+ <>
+
+
+
+
+ >
+)
+Disabled.args = {
+ disabled: true,
+}
+Disabled.parameters = {
+ docs: {
+ description: {
+ story: "Use disabled buttons when an action is being prevented for some reason. \
+ Always communicate to the user why the button can't be clicked. This can \
+ be done through a tooltip on hover, or with supplementary text underneath \
+ the button. Do not change the button label between disabled/enabled states.",
+ },
+ },
+}
+
+export const Small = Template.bind({})
+Small.args = {
+ small: true,
+ name: 'Small button',
+}
+Small.parameters = {
+ docs: {
+ description: {
+ story: 'Buttons are available in three sizes: `small`, `medium`, and `large`. \
+ Medium is usually the correct choice. Use small buttons in an information-\
+ dense ui.',
+ },
+ },
+}
+
+export const Large = Template.bind({})
+Large.args = {
+ large: true,
+ name: 'Large button',
+}
+Large.parameters = {
+ docs: {
+ description: {
+ story: 'Buttons are available in three sizes: `small`, `medium`, and `large`. \
+ Medium is usually the correct choice. Large buttons can be used on very simple, \
+ single-action pages.',
+ },
+ },
+}
+
+export const InitialFocus = Template.bind({})
+InitialFocus.args = {
+ initialFocus: true,
+ name: 'Focused button',
+}
+// When enabled, this story grabs focus every time a control is changed
+// in the docs page. Disabled for better UX
+InitialFocus.parameters = { docs: { disable: true } }
+
+export const Icon = (args) => (
+ <>
+
+
+
+
+ >
+)
+Icon.args = {
+ icon: DemoIcon24,
+ name: 'Icon button',
+}
+Icon.parameters = {
+ docs: {
+ description: {
+ story: 'Icons can be included in Basic, Primary, Secondary and Destructive buttons. \
+ Use an icon to supplement the text label. Remember that the user may not be \
+ fluent in the working language, so an accompanying icon on an important action \
+ can be a welcome addition. Buttons with icons only should be used for \
+ supplementary actions and should include a text tooltip on hover.',
+ },
+ },
+}
+
+export const IconOnly = Template.bind({})
+IconOnly.args = {
+ icon: DemoIcon24,
+ name: 'Icon only button',
+ children: null,
+}
+
+export const IconSmall = Template.bind({})
+IconSmall.args = {
+ icon: DemoIcon16,
+ small: true,
+ name: 'Icon small button',
+ children: 'Label me!',
+}
+
+export const IconOnlySmall = Template.bind({})
+IconOnlySmall.args = {
+ icon: DemoIcon16,
+ small: true,
+ name: 'Icon only small button',
+ children: null,
+}
+
+export const Toggled = Template.bind({})
+Toggled.args = {
+ toggled: true,
+ icon: DemoIcon24,
+ name: 'Toggled button',
+ children: null,
+}
+Toggled.parameters = {
+ docs: {
+ description: {
+ story: 'A button can represent an on/off state using the toggle option. \
+ Use a toggle button when the user can enable or disable an option and \
+ a checkbox or switch is not suitable. This will most often be in the case of \
+ a toolbar, such as bold or italic options in a text editing toolbar. \
+ A toggle button in this example uses an icon and does not need text. \
+ A text label should be provided in a tooltip on hover. The toggle option \
+ is available for basic and secondary type buttons.',
+ },
+ },
+}
+
+export const ToggledDisabled = Template.bind({})
+ToggledDisabled.args = {
+ toggled: true,
+ disabled: true,
+ icon: DemoIcon24,
+ name: 'Toggled button',
+ children: null,
+}
+
+export const Loading = (args) => (
+ <>
+
+
+
+
+ >
+)
+Loading.args = {
+ name: 'Loading button',
+ loading: true,
+ children: 'Loading...',
+ icon: DemoIcon24,
+}
+Loading.parameters = {
+ docs: {
+ description: {
+ story: 'A button can be in a loading state. Use the loading state to show a pending action after the button has been triggered. The button text should change to let the user know what is happening. For example, a button labelled "Send" might changed to "Sending..." when in a loading state.',
+ },
+ },
+}
diff --git a/components/button/src/native-button/button.styles.js b/components/button/src/native-button/button.styles.js
new file mode 100644
index 0000000000..805489d77d
--- /dev/null
+++ b/components/button/src/native-button/button.styles.js
@@ -0,0 +1,71 @@
+import { colors, theme, spacers } from '@dhis2/ui-constants'
+import css from 'styled-jsx/css'
+
+export default css`
+ button {
+ display: inline-flex;
+ position: relative;
+ align-items: center;
+ justify-content: center;
+ border-radius: 4px;
+ font-weight: 400;
+ letter-spacing: 0.5px;
+ text-decoration: none;
+ cursor: pointer;
+ user-select: none;
+ color: ${colors.grey900};
+ height: 36px;
+ padding: 0 ${spacers.dp12};
+ font-size: 14px;
+ line-height: 16px;
+ border: 1px solid ${colors.grey500};
+ background-color: #f9fafb;
+ }
+
+ button:disabled {
+ cursor: not-allowed;
+ }
+
+ button:focus {
+ outline: 3px solid ${theme.focus};
+ outline-offset: -3px;
+ text-decoration: underline;
+ }
+
+ /* Prevent focus styles when mouse clicking */
+ button:focus:not(:focus-visible) {
+ outline: none;
+ text-decoration: none;
+ }
+
+ /* Prevent focus styles on active and disabled buttons */
+ button:active:focus,
+ button:disabled:focus {
+ outline: none;
+ text-decoration: none;
+ }
+
+ button:hover {
+ border-color: ${colors.grey500};
+ background-color: ${colors.grey200};
+ }
+
+ button:active,
+ button:active:focus {
+ border-color: ${colors.grey500};
+ background-color: ${colors.grey200};
+ box-shadow: 0 0 0 1px rgb(0, 0, 0, 0.1) inset;
+ }
+
+ button:focus {
+ background-color: #f9fafb;
+ }
+
+ button:disabled {
+ border-color: ${colors.grey400};
+ background-color: #f9fafb;
+ box-shadow: none;
+ color: ${theme.disabled};
+ fill: ${theme.disabled};
+ }
+`
diff --git a/components/button/src/native-button/index.js b/components/button/src/native-button/index.js
new file mode 100644
index 0000000000..92744318c1
--- /dev/null
+++ b/components/button/src/native-button/index.js
@@ -0,0 +1 @@
+export { NativeButton } from './native-button.js'
diff --git a/components/button/src/native-button/native-button.js b/components/button/src/native-button/native-button.js
new file mode 100644
index 0000000000..b87828ebb3
--- /dev/null
+++ b/components/button/src/native-button/native-button.js
@@ -0,0 +1,19 @@
+import PropTypes from 'prop-types'
+import React, { forwardRef } from 'react'
+import styles from './button.styles.js'
+
+export const NativeButton = forwardRef(function NativeButton(props, ref) {
+ const { children, ...rest } = props
+
+ return (
+
+ )
+})
+
+NativeButton.propTypes = {
+ /** Component to render inside the button */
+ children: PropTypes.node,
+}