diff --git a/frontend/documentation/components/Link.stories.tsx b/frontend/documentation/components/Link.stories.tsx new file mode 100644 index 000000000000..fd9d9a364dba --- /dev/null +++ b/frontend/documentation/components/Link.stories.tsx @@ -0,0 +1,114 @@ +import React, { ComponentProps } from 'react' +import type { Meta, StoryObj } from 'storybook' + +import Link from 'components/Link' +import Icon from 'components/icons/Icon' + +type LinkProps = ComponentProps + +const meta: Meta = { + argTypes: { + children: { + control: 'text', + description: 'Link content. Composes naturally with text and icons.', + }, + external: { + control: 'boolean', + description: + 'Forces or suppresses external behaviour. Defaults to auto-detect: `true` for `http(s)://` URLs, `false` otherwise.', + }, + href: { + control: 'text', + description: 'Link target. Internal paths or external URLs.', + }, + noUnderline: { + control: 'boolean', + description: 'Drops the hover underline.', + }, + }, + args: { + children: 'View docs', + href: 'https://docs.flagsmith.com', + }, + component: Link, + parameters: { + docs: { + description: { + component: + 'Anchor primitive. Children-only — icons compose via JSX, no `iconLeft`/`iconRight` props. External-vs-internal behaviour auto-detects from the URL.', + }, + }, + layout: 'centered', + }, + title: 'Components/Navigation/Link', +} +export default meta + +type Story = StoryObj + +export const Default: Story = {} + +export const Internal: Story = { + args: { + children: 'Go to features', + href: '/features', + }, + parameters: { + docs: { + description: { + story: + 'Internal route — does NOT add `target="_blank"` (auto-detected from non-`http(s)://` href).', + }, + }, + }, +} + +export const Inline: Story = { + parameters: { + docs: { + description: { + story: + 'Inline composition with surrounding text — typical "see the docs" callout.', + }, + }, + }, + render: () => ( +

+ Permission groups can be configured at the project level. See{' '} + + the RBAC docs + {' '} + for details. +

+ ), +} + +export const WithIcon: Story = { + parameters: { + docs: { + description: { + story: + 'Icon + label via children composition — no `iconLeft`/`iconRight` props. Spacing handled by the link wrapper.', + }, + }, + }, + render: () => ( + + Open in new tab + + ), +} + +export const NoUnderline: Story = { + args: { + noUnderline: true, + }, + parameters: { + docs: { + description: { + story: + 'Hides the hover underline — useful for links inside dense layouts where the underline is too noisy.', + }, + }, + }, +} diff --git a/frontend/web/components/AdminAPIKeys.js b/frontend/web/components/AdminAPIKeys.js index f8bbb7beb7e6..e0f691d18b1a 100644 --- a/frontend/web/components/AdminAPIKeys.js +++ b/frontend/web/components/AdminAPIKeys.js @@ -5,6 +5,7 @@ import data from 'common/data/base/_data' import Token from './Token' import JSONReference from './JSONReference' import Button from './base/forms/Button' +import Link from './Link' import DateSelect from './DateSelect' import Icon from './icons/Icon' import Switch from './Switch' @@ -431,14 +432,9 @@ export default class AdminAPIKeys extends PureComponent { {`API keys are used to authenticate with the Admin API.`}

- +
+

= (props) => {

{title}

- {description}{' '} - {docs && ( - - )} + {description} {docs && View docs}
{renderActions()}
@@ -300,13 +291,13 @@ const Integration: FC = (props) => { {`Integration ${ props.lastSaved.isCreate ? 'added to' : 'saved to' } ${lastSavedProject?.name ?? 'your project'}.\u00A0`} - Manage in project integrations - + {props.onDismissLastSaved && ( ['onClick'] +} + +const Link: FC = ({ + children, + className, + external, + href, + noUnderline = false, + onClick, + rel, + target, +}) => { + const isExternal = external ?? /^https?:\/\//.test(href) + return ( + + {children} + + ) +} + +export default Link diff --git a/frontend/web/components/modals/AuditLogWebhooks.tsx b/frontend/web/components/modals/AuditLogWebhooks.tsx index e13467409e05..d8624ec78630 100644 --- a/frontend/web/components/modals/AuditLogWebhooks.tsx +++ b/frontend/web/components/modals/AuditLogWebhooks.tsx @@ -1,6 +1,7 @@ import React, { FC } from 'react' import JSONReference from 'components/JSONReference' import Button from 'components/base/forms/Button' +import Link from 'components/Link' import Icon from 'components/icons/Icon' import Constants from 'common/constants' import { @@ -74,13 +75,9 @@ const AuditLogWebhooks: FC = ({ organisationId }) => { Audit webhooks let you know when audit logs occur. You can configure 1 or more audit webhooks per organisation.
- +

diff --git a/frontend/web/components/modals/InviteUsers.tsx b/frontend/web/components/modals/InviteUsers.tsx index 5f48e66113f3..b5e90295fe0f 100644 --- a/frontend/web/components/modals/InviteUsers.tsx +++ b/frontend/web/components/modals/InviteUsers.tsx @@ -1,5 +1,6 @@ import React, { useState, useEffect, useRef, FC } from 'react' import Button from 'components/base/forms/Button' +import Link from 'components/Link' import ConfigProvider from 'common/providers/ConfigProvider' import Constants from 'common/constants' import Icon from 'components/icons/Icon' @@ -297,14 +298,9 @@ const InviteUsers: FC = () => { Users without administrator privileges will need to be invited to individual projects.
- +
{error && } diff --git a/frontend/web/components/pages/AccountSettingsPage.tsx b/frontend/web/components/pages/AccountSettingsPage.tsx index b3ada04700e0..5a3f9dc9c6b9 100644 --- a/frontend/web/components/pages/AccountSettingsPage.tsx +++ b/frontend/web/components/pages/AccountSettingsPage.tsx @@ -316,14 +316,9 @@ const AccountSettingsPage: FC = () => {

You can use this token to securely integrate with the private endpoints of our{' '} - + .

diff --git a/frontend/web/components/pages/EnvironmentSettingsPage.tsx b/frontend/web/components/pages/EnvironmentSettingsPage.tsx index c7a465892b48..1ca26b05b4ef 100644 --- a/frontend/web/components/pages/EnvironmentSettingsPage.tsx +++ b/frontend/web/components/pages/EnvironmentSettingsPage.tsx @@ -18,7 +18,7 @@ import PageTitle from 'components/PageTitle' import { getStore } from 'common/store' import { getRoles } from 'common/services/useRole' import AccountStore from 'common/stores/account-store' -import { Link, useHistory, useRouteMatch } from 'react-router-dom' +import { Link as RouterLink, useHistory, useRouteMatch } from 'react-router-dom' import { enableFeatureVersioning } from 'common/services/useEnableFeatureVersioning' import AddMetadataToEntity from 'components/metadata/AddMetadataToEntity' import { getSupportedContentType } from 'common/services/useSupportedContentType' @@ -41,6 +41,7 @@ import { useUpdateWebhookMutation, } from 'common/services/useWebhooks' import Button from 'components/base/forms/Button' +import Link from 'components/Link' import Input from 'components/base/forms/Input' import { useGetEnvironmentQuery } from 'common/services/useEnvironment' import { useRouteContext } from 'components/providers/RouteContext' @@ -658,9 +659,9 @@ const EnvironmentSettingsPage: React.FC = () => { enabling this will prevent the API from returning features that are disabled. You can also manage this in{' '} - + Project settings - + .

@@ -729,14 +730,9 @@ const EnvironmentSettingsPage: React.FC = () => {
For full information on the excluded fields see documentation{' '} - +
Enabling this feature will change the response from the API and could break your existing @@ -795,14 +791,9 @@ const EnvironmentSettingsPage: React.FC = () => { Feature webhooks let you know when features have changed. You can configure 1 or more Feature Webhooks per Environment.{' '} - +

+
@@ -246,7 +242,7 @@ const IdentitiesPage: FC<{ props: any }> = (props) => { key={id} data-test={`user-item-${index}`} > - = (props) => {
)} - +
+ . Segments can be used to override features within the features page for any environment.{' '} - +
{isLoading && !segments && !searchInput && ( diff --git a/frontend/web/components/pages/UsersAndPermissionsPage.tsx b/frontend/web/components/pages/UsersAndPermissionsPage.tsx index e958bd489964..86357a2d0a4a 100644 --- a/frontend/web/components/pages/UsersAndPermissionsPage.tsx +++ b/frontend/web/components/pages/UsersAndPermissionsPage.tsx @@ -1,6 +1,7 @@ import React, { FC, useState } from 'react' import JSONReference from 'components/JSONReference' import Button from 'components/base/forms/Button' +import Link from 'components/Link' import Tabs from 'components/navigation/TabMenu/Tabs' import TabItem from 'components/navigation/TabMenu/TabItem' import Utils from 'common/utils/utils' @@ -170,14 +171,9 @@ const UsersAndPermissionsInner: FC = ({ Flagsmith lets you manage fine-grained permissions for your projects and environments, invite members as a user or an administrator and then set permission in your Project and Environment settings.{' '} - +

@@ -234,6 +230,7 @@ const UsersAndPermissionsInner: FC = ({ for your plan.{' '} {usedSeats && ( <> + {/* eslint-disable-next-line no-nested-ternary */} {overSeats && (!verifySeatsLimit || !autoSeats) ? ( @@ -402,14 +399,9 @@ const UsersAndPermissionsInner: FC = ({ Anyone with link can join as a standard user, once they have joined you can edit their role from the team members panel.{' '} - +

{error && } diff --git a/frontend/web/components/pages/sdk-keys/SDKKeysPage.tsx b/frontend/web/components/pages/sdk-keys/SDKKeysPage.tsx index 604857c6b1ef..3e1c045f99f4 100644 --- a/frontend/web/components/pages/sdk-keys/SDKKeysPage.tsx +++ b/frontend/web/components/pages/sdk-keys/SDKKeysPage.tsx @@ -1,5 +1,6 @@ import React, { FC } from 'react' import Button from 'components/base/forms/Button' +import Link from 'components/Link' import Input from 'components/base/forms/Input' import Icon from 'components/icons/Icon' import PageTitle from 'components/PageTitle' @@ -37,13 +38,9 @@ const SDKKeysPage: FC = () => { > Use this key to initialise{' '} - {' '} + {' '} SDKs.
diff --git a/frontend/web/components/pages/sdk-keys/components/ServerSideSDKKeys.tsx b/frontend/web/components/pages/sdk-keys/components/ServerSideSDKKeys.tsx index 6a21a8585ad0..e683130051f8 100644 --- a/frontend/web/components/pages/sdk-keys/components/ServerSideSDKKeys.tsx +++ b/frontend/web/components/pages/sdk-keys/components/ServerSideSDKKeys.tsx @@ -1,5 +1,6 @@ import React, { FC } from 'react' import Button from 'components/base/forms/Button' +import Link from 'components/Link' import Tooltip from 'components/Tooltip' import Constants from 'common/constants' import { useHasPermission } from 'common/providers/Permission' @@ -63,13 +64,9 @@ const ServerSideSDKKeys: FC = ({

Flags can be evaluated locally within your own Server environments using our{' '} - + .