Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(main-nav): Main Nav refactoring, add the Home icon #20176

Merged
merged 13 commits into from Apr 29, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
19 changes: 11 additions & 8 deletions docs/docs/guides/05-type-system/02-concepts/01-schema.mdx
Expand Up @@ -67,8 +67,8 @@ For instance, a string attribute will resolve to a primitive string in an entity

### Usage

import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

<Tabs>
<TabItem value="public" label="Public" default>
Expand All @@ -89,29 +89,31 @@ declare const component: Schema.Component;

declare function processAnySchema(schema: Schema.Schema): void;

processAnySchema(schema); // ✅
processAnySchema(schema); // ✅
processAnySchema(contentType); // ✅
processAnySchema(component); // ✅

declare function processContentTypeSchema(schema: Schema.ContentType): void;

processContentTypeSchema(schema); // ✅
processContentTypeSchema(schema); // ✅
processContentTypeSchema(contentType); // ✅
processContentTypeSchema(component); // ❌ Error, a component schema is not assignable to a content-type schema

declare function processComponentSchema(schema: Schema.Component): void;

processComponentSchema(schema); // ✅
processComponentSchema(schema); // ✅
processComponentSchema(contentType); // ❌ Error, a content-type schema is not assignable to a component schema
processComponentSchema(component); // ✅
```

</TabItem>
<TabItem value="internal" label="Internal">
Schema definitions exported from the `Struct` namespace defines the low level type representation of Strapi schemas.

:::caution
Those types can be useful when you want to validate other types against the base ones, but realistically, the public Schema types should almost always be preferred.
:::

```typescript
import type { Struct } from '@strapi/strapi';

Expand All @@ -121,21 +123,22 @@ declare const component: Struct.ComponentSchema;

declare function processAnySchema(schema: Struct.Schema): void;

processAnySchema(schema); // ✅
processAnySchema(schema); // ✅
processAnySchema(contentType); // ✅
processAnySchema(component); // ✅

declare function processContentTypeSchema(schema: Struct.ContentTypeSchema): void;

processContentTypeSchema(schema); // ✅
processContentTypeSchema(schema); // ✅
processContentTypeSchema(contentType); // ✅
processContentTypeSchema(component); // ❌ Error, a component schema is not assignable to a content-type schema

declare function processComponentSchema(schema: Struct.ComponentSchema): void;

processComponentSchema(schema); // ✅
processComponentSchema(schema); // ✅
processComponentSchema(contentType); // ❌ Error, a content-type schema is not assignable to a component schema
processComponentSchema(component); // ✅
```

</TabItem>
</Tabs>
18 changes: 10 additions & 8 deletions docs/docs/guides/05-type-system/02-concepts/02-uid.mdx
Expand Up @@ -14,7 +14,6 @@ On this page, **a resource** is considered as **anything that can be identified
This includes (but is not limited to) controllers, schema, services, policies, middlewares, etc...
:::


In the Type System, UIDs play a crucial role in referencing various resources (such as schema and entities) by attaching a unique identifier.

To put it simply, a UID is a unique (string) literal key used to identify, locate, or access a particular resource within the system.
Expand All @@ -26,6 +25,7 @@ This makes it the perfect tool to index type registries or to use as a type para
### Format

A UID is composed of 3 different parts:

1. A namespace ([link](#1-namespaces))
2. A separator ([link](#2-separators))
3. A name ([link](#3-names))
Expand All @@ -46,7 +46,7 @@ Scoped namespaces are defined by a base name, followed by a separator (`::`) and
In Strapi there are two of them:

| Name | Definition | Description |
|--------|:-----------------:|------------------------------------------------------|
| ------ | :---------------: | ---------------------------------------------------- |
| API | `api::<scope>` | Represent a resource present in the `<scope>` API |
| Plugin | `plugin::<scope>` | Represent a resource present in the `<scope>` plugin |

Expand All @@ -57,7 +57,7 @@ These namespaces are used as a simple prefix and define the origin of a resource
Strapi uses three of them to create UIDs

| Name | Definition | Description |
|--------|:----------:|-------------------------------------------------------------------------------|
| ------ | :--------: | ----------------------------------------------------------------------------- |
| Strapi | `strapi` | Represent a resource present in the core of strapi |
| Admin | `admin` | Represent a resource present in Strapi admin |
| Global | `global` | Rarely used (_e.g. policies or middlewares_), it represents a global resource |
Expand Down Expand Up @@ -90,7 +90,7 @@ ContentType and Component are referring to both the related schema and entity.
:::

| | ContentType | Component | Middleware | Policy | Controller | Service |
|--------------------------|:------------------:|:------------------:|:------------------:|:------------------:|:------------------:|:------------------:|
| ------------------------ | :----------------: | :----------------: | :----------------: | :----------------: | :----------------: | :----------------: |
| `api::<scope>.<name>` | :white_check_mark: | :x: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| `plugin::<scope>.<name>` | :white_check_mark: | :x: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| `<category>.<name>` | :x: | :white_check_mark: | :x: | :x: | :x: | :x: |
Expand Down Expand Up @@ -141,6 +141,7 @@ fetch('api::article.article');
fetch('admin::user');
// ^ this should return a Data.Entity<'admin::user'>
```

To do that, we'll need the function to be able to provide us with the current `uid` type based on usage.

```typescript
Expand Down Expand Up @@ -171,14 +172,15 @@ Let's add the possibility to select which fields we want to return for our entit
```typescript
import type { UID, Data, Schema } from '@strapi/types';

declare function fetch<
T extends UID.ContentType,
F extends Schema.AttributeNames<T>
>(uid: T, fields: F[]): Data.ContentType<T>;
declare function fetch<T extends UID.ContentType, F extends Schema.AttributeNames<T>>(
uid: T,
fields: F[]
): Data.ContentType<T>;
```

:::tip
You may have noticed that we're using the inferred UID type (`T`) to reference both:

- An entity (`Data.Entity<T>`)
- A schema (`Schema.AttributeNames<T>`)

Expand Down
64 changes: 36 additions & 28 deletions docs/docs/guides/05-type-system/02-concepts/03-public-registry.mdx
Expand Up @@ -9,8 +9,8 @@ tags:
toc_max_heading_level: 5
---

import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

### Context

Expand Down Expand Up @@ -108,6 +108,7 @@ Creating a new registry is as simple as exporting an indexed interface from the
Let's declare the content-type schema registry.

It should accept:

- Content-type UIDs as keys
- Content-type schemas as values

Expand All @@ -130,7 +131,7 @@ We use low level types to define our index (`Internal`/`Struct`) to keep it as g
To define `UID.ContentType`, we extract every key (`Internal.Registry.Keys`) from the public content-type registry (`Public.ContentTypeSchemas`) that matches with the base definition of a content-type UID (`Internal.UID.ContentType`).

```ts title="@strapi/types/uid.ts"
import type { Internal, Public } from '@strapi/types'
import type { Internal, Public } from '@strapi/types';

export type ContentType = Internal.Registry.Keys<
Public.ContentTypeSchemas,
Expand All @@ -151,7 +152,7 @@ Since `UID.ContentType` (`TUID`) is [dynamically built based on actual keys](#ui
:::

```ts title="@strapi/types/schema.ts"
import type { UID, Public } from '@strapi/types'
import type { UID, Public } from '@strapi/types';

export type ContentType<TUID extends UID.ContentType> = Public.ContentTypeSchemas[TUID];
```
Expand All @@ -172,6 +173,7 @@ Remember to use dynamic type definitions (`UID`, `Data`, `Schema`) and not stati

:::info[Reminder]
Registries are **indexed**, which means that:

- **When augmented** (_e.g. in users' applications_), they'll return **strongly typed values** that correspond to the defined types.
- **When empty** (_e.g. in Strapi codebase_), they'll return **generic low level types** based on their index definition.

Expand All @@ -183,6 +185,7 @@ Registries are **indexed**, which means that:
const uid: UID.ContentType;
// ^ 'api::article.article' | 'admin::user'
```

</TabItem>
<TabItem value="empty" label="Empty">
```ts
Expand All @@ -191,6 +194,7 @@ Registries are **indexed**, which means that:
const uid: UID.ContentType;
// ^ `admin::${string}` | `api::${string}.${string}` | `plugin::${string}.${string}` | `strapi::${string}`
```

</TabItem>
</Tabs>
:::
Expand All @@ -215,7 +219,7 @@ declare module '@strapi/strapi' {
}
}
}
````
```

This will force every type that depends on the `Public.ContentTypeSchemas` registry to recognize `'api::article.article'` as the only valid UID and `ApiArticleArticle` the only valid schema.

Expand All @@ -235,18 +239,20 @@ The process will generate type definitions based on the user application state (
<TabItem value="manual" label="Manual Run">
Generate the types once.

```shell title="my-app/"
yarn strapi ts:generate-types
```
```shell title="my-app/"
yarn strapi ts:generate-types
```

</TabItem>
<TabItem value="dev" label="During Development">
Start the application in dev mode, and generate types on every server restart.

Useful when working with the content-type builder.

```shell title="my-app/"
yarn develop
```
```shell title="my-app/"
yarn develop
```

</TabItem>
</Tabs>

Expand Down Expand Up @@ -277,7 +283,6 @@ declare module '@strapi/strapi' {

When coupling everything together, the end result is a TypeScript developer experience automatically adjusted to the current context.


<Tabs>
<TabItem value="app" label="User Application">
```ts title="my-app/src/index.ts"
Expand All @@ -292,25 +297,28 @@ When coupling everything together, the end result is a TypeScript developer expe
strapi.findOne('api::blog.blog');
// ^ Error, TypeScript will complain
}
})
```
</TabItem>
<TabItem value="strapi" label="Strapi Codebase">
```ts title="@strapi/strapi/document-service.ts"
import type { UID } from '@strapi/types';

export const findOne<TUID extends UID.ContentType>(uid: TUID) {
// ...
}
})

````
</TabItem>
<TabItem value="strapi" label="Strapi Codebase">
```ts title="@strapi/strapi/document-service.ts"
import type { UID } from '@strapi/types';

findOne('admin::foo');
// ^ Valid, matches 'admin::${string}'
export const findOne<TUID extends UID.ContentType>(uid: TUID) {
// ...
}

findOne('admin::foo');
// ^ Valid, matches 'admin::${string}'

findOne('plugin::bar.bar');
// ^ Valid, matches 'plugin::${string}.${string}'
findOne('plugin::bar.bar');
// ^ Valid, matches 'plugin::${string}.${string}'

findOne('baz');
// ^ Error, does not correspond to any content-type UID format
````

findOne('baz');
// ^ Error, does not correspond to any content-type UID format
```
</TabItem>
</Tabs>
8 changes: 7 additions & 1 deletion packages/core/admin/admin/src/components/LeftMenu.tsx
Expand Up @@ -11,7 +11,7 @@ import {
NavSections,
NavUser,
} from '@strapi/design-system/v2';
import { Exit, Write, Lock } from '@strapi/icons';
import { Exit, Write, Lock, House } from '@strapi/icons';
import { useIntl } from 'react-intl';
import { NavLink as RouterNavLink, useLocation } from 'react-router-dom';
import styled from 'styled-components';
Expand All @@ -24,6 +24,7 @@ import { usePersistentState } from '../hooks/usePersistentState';
import { getDisplayName } from '../utils/users';

import { NavBrand as NewNavBrand } from './MainNav/NavBrand';
import { NavLink as NewNavLink } from './MainNav/NavLink';

const LinkUserWrapper = styled(Box)`
width: ${150 / 16}rem;
Expand Down Expand Up @@ -135,6 +136,11 @@ const LeftMenu = ({ generalSectionLinks, pluginsSectionLinks }: LeftMenuProps) =
<Divider />

<NavSections>
{condensed && (
<NewNavLink to="/" icon={House} onClick={() => handleClickOnLink('/')}>
{formatMessage({ id: 'global.home', defaultMessage: 'Home' })}
</NewNavLink>
)}
<NavLink
as={RouterNavLink}
// @ts-expect-error the props from the passed as prop are not inferred // joined together
Expand Down