Skip to content

Commit 2dfd5c7

Browse files
nick-skriabinbmartelyyassi-heartex
authored
feat: DIA-1783: [FE] Prep work - Tailwind + shadcn cross integration in LSO + LSE (#6870)
Co-authored-by: bmartel <brandonmartel@gmail.com> Co-authored-by: yyassi-heartex <104568407+yyassi-heartex@users.noreply.github.com>
1 parent e005678 commit 2dfd5c7

35 files changed

+4776
-230
lines changed

Dockerfile.development

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
# syntax=docker/dockerfile:1
2+
ARG NODE_VERSION=18
3+
ARG PYTHON_VERSION=3.12
4+
ARG POETRY_VERSION=1.8.4
5+
ARG VERSION_OVERRIDE
6+
ARG BRANCH_OVERRIDE
7+
8+
################################ Overview
9+
10+
# This Dockerfile builds a Label Studio environment.
11+
# It consists of three main stages:
12+
# 1. "frontend-builder" - Compiles the frontend assets using Node.
13+
# 2. "frontend-version-generator" - Generates version files for frontend sources.
14+
# 3. "venv-builder" - Prepares the virtualenv environment.
15+
# 4. "py-version-generator" - Generates version files for python sources.
16+
# 5. "prod" - Creates the final production image with the Label Studio, Nginx, and other dependencies.
17+
18+
################################ Stage: frontend-builder (build frontend assets)
19+
FROM --platform=${BUILDPLATFORM} node:${NODE_VERSION} AS frontend-builder
20+
WORKDIR /label-studio/web
21+
22+
COPY web .
23+
COPY pyproject.toml ../pyproject.toml
24+
25+
################################ Stage: venv-builder (prepare the virtualenv)
26+
FROM python:${PYTHON_VERSION}-slim AS venv-builder
27+
ARG POETRY_VERSION
28+
29+
ENV PYTHONUNBUFFERED=1 \
30+
PYTHONDONTWRITEBYTECODE=1 \
31+
PIP_NO_CACHE_DIR=off \
32+
PIP_DISABLE_PIP_VERSION_CHECK=on \
33+
PIP_DEFAULT_TIMEOUT=100 \
34+
PIP_CACHE_DIR="/.cache" \
35+
POETRY_CACHE_DIR="/.poetry-cache" \
36+
POETRY_HOME="/opt/poetry" \
37+
POETRY_VIRTUALENVS_IN_PROJECT=true \
38+
PATH="/opt/poetry/bin:$PATH"
39+
40+
ADD https://install.python-poetry.org /tmp/install-poetry.py
41+
RUN python /tmp/install-poetry.py
42+
43+
RUN --mount=type=cache,target="/var/cache/apt",sharing=locked \
44+
--mount=type=cache,target="/var/lib/apt/lists",sharing=locked \
45+
set -eux; \
46+
apt-get update; \
47+
apt-get install --no-install-recommends -y \
48+
build-essential git; \
49+
apt-get autoremove -y
50+
51+
WORKDIR /label-studio
52+
53+
ENV VENV_PATH="/label-studio/.venv"
54+
ENV PATH="$VENV_PATH/bin:$PATH"
55+
56+
## Starting from this line all packages will be installed in $VENV_PATH
57+
58+
# Copy dependency files
59+
COPY pyproject.toml poetry.lock README.md ./
60+
61+
# Install dependencies without dev packages
62+
RUN --mount=type=cache,target=$POETRY_CACHE_DIR,sharing=locked \
63+
poetry check --lock && poetry install --no-root --without test --extras uwsgi
64+
65+
# Install LS
66+
COPY label_studio label_studio
67+
RUN --mount=type=cache,target=$POETRY_CACHE_DIR,sharing=locked \
68+
# `--extras uwsgi` is mandatory here due to poetry bug: https://github.com/python-poetry/poetry/issues/7302
69+
poetry install --only-root --extras uwsgi && \
70+
python3 label_studio/manage.py collectstatic --no-input
71+
72+
################################ Stage: py-version-generator
73+
FROM venv-builder AS py-version-generator
74+
ARG VERSION_OVERRIDE
75+
ARG BRANCH_OVERRIDE
76+
77+
# Create version_.py and ls-version_.py
78+
RUN --mount=type=bind,source=.git,target=./.git \
79+
VERSION_OVERRIDE=${VERSION_OVERRIDE} BRANCH_OVERRIDE=${BRANCH_OVERRIDE} poetry run python label_studio/core/version.py
80+
81+
################################### Stage: prod
82+
FROM python:${PYTHON_VERSION}-slim AS production
83+
84+
ENV LS_DIR=/label-studio \
85+
HOME=/label-studio \
86+
LABEL_STUDIO_BASE_DATA_DIR=/label-studio/data \
87+
OPT_DIR=/opt/heartex/instance-data/etc \
88+
PATH="/label-studio/.venv/bin:$PATH" \
89+
DJANGO_SETTINGS_MODULE=core.settings.label_studio \
90+
PYTHONUNBUFFERED=1 \
91+
PYTHONDONTWRITEBYTECODE=1
92+
93+
WORKDIR $LS_DIR
94+
95+
# install prerequisites for app
96+
RUN --mount=type=cache,target="/var/cache/apt",sharing=locked \
97+
--mount=type=cache,target="/var/lib/apt/lists",sharing=locked \
98+
set -eux; \
99+
apt-get update; \
100+
apt-get upgrade -y; \
101+
apt-get install --no-install-recommends -y libexpat1 \
102+
gnupg2 curl; \
103+
apt-get autoremove -y
104+
105+
# install nginx
106+
RUN --mount=type=cache,target="/var/cache/apt",sharing=locked \
107+
--mount=type=cache,target="/var/lib/apt/lists",sharing=locked \
108+
set -eux; \
109+
curl -sSL https://nginx.org/keys/nginx_signing.key | gpg --dearmor -o /etc/apt/keyrings/nginx-archive-keyring.gpg >/dev/null; \
110+
DEBIAN_VERSION=$(awk -F '=' '/^VERSION_CODENAME=/ {print $2}' /etc/os-release); \
111+
printf "deb [signed-by=/etc/apt/keyrings/nginx-archive-keyring.gpg] http://nginx.org/packages/debian ${DEBIAN_VERSION} nginx\n" > /etc/apt/sources.list.d/nginx.list; \
112+
printf "Package: *\nPin: origin nginx.org\nPin: release o=nginx\nPin-Priority: 900\n" > /etc/apt/preferences.d/99nginx; \
113+
apt-get update; \
114+
apt-get install --no-install-recommends -y nginx; \
115+
apt-get autoremove -y
116+
117+
RUN set -eux; \
118+
mkdir -p $LS_DIR $LABEL_STUDIO_BASE_DATA_DIR $OPT_DIR && \
119+
chown -R 1001:0 $LS_DIR $LABEL_STUDIO_BASE_DATA_DIR $OPT_DIR /var/log/nginx /etc/nginx
120+
121+
COPY --chown=1001:0 deploy/default.conf /etc/nginx/nginx.conf
122+
123+
# Copy essential files for installing Label Studio and its dependencies
124+
COPY --chown=1001:0 pyproject.toml .
125+
COPY --chown=1001:0 poetry.lock .
126+
COPY --chown=1001:0 README.md .
127+
COPY --chown=1001:0 LICENSE LICENSE
128+
COPY --chown=1001:0 licenses licenses
129+
COPY --chown=1001:0 deploy deploy
130+
131+
# Copy files from build stages
132+
COPY --chown=1001:0 --from=venv-builder $LS_DIR $LS_DIR
133+
COPY --chown=1001:0 --from=py-version-generator $LS_DIR/label_studio/core/version_.py $LS_DIR/label_studio/core/version_.py
134+
COPY --chown=1001:0 --from=frontend-builder $LS_DIR/web/dist $LS_DIR/web/dist
135+
136+
USER 1001
137+
138+
EXPOSE 8080
139+
140+
ENTRYPOINT ["./deploy/docker-entrypoint.sh"]
141+
CMD ["label-studio"]

web/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ tmp
66

77
# dependencies
88
node_modules
9+
dist
910

1011
# IDEs and editors
1112
/.idea

web/.stylelintrc.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
{
2-
"extends": "stylelint-config-standard-scss",
2+
"extends": ["stylelint-config-tailwindcss/scss", "stylelint-config-standard-scss"],
33
"rules": {
44
"selector-class-pattern": null,
55
"custom-property-pattern": null,
66
"no-descending-specificity": null,
77
"scss/no-global-function-names": null,
8+
"scss/function-no-unknown": null,
89
"selector-pseudo-class-no-unknown": [
910
true,
1011
{

web/apps/labelstudio/src/app/App.jsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { RootPage } from "./RootPage";
1919
import { FF_OPTIC_2, FF_UNSAVED_CHANGES, FF_PRODUCT_TOUR, isFF } from "../utils/feature-flags";
2020
import { TourProvider } from "@humansignal/core";
2121
import { ToastProvider, ToastViewport } from "@humansignal/ui";
22+
import "@humansignal/ui/src/tailwind.css";
2223

2324
const baseURL = new URL(APP_SETTINGS.hostname || location.origin);
2425
export const UNBLOCK_HISTORY_MESSAGE = "UNBLOCK_HISTORY";

web/apps/labelstudio/src/components/Menubar/Menubar.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ export const Menubar = ({ enabled, defaultOpened, defaultPinned, children, onSid
132132
<div className={menubarClass}>
133133
<Dropdown.Trigger dropdown={menuDropdownRef} closeOnClickOutside={!sidebarPinned}>
134134
<div className={`${menubarClass.elem("trigger")} main-menu-trigger`}>
135-
<img src={absoluteURL("/static/icons/logo.svg")} alt="Label Studio Logo" height="22" />
135+
<img src={absoluteURL("/static/icons/logo.svg")} alt="Label Studio Logo" style={{ height: 22 }} />
136136
<Hamburger opened={sidebarOpened} />
137137
</div>
138138
</Dropdown.Trigger>

web/components.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"$schema": "https://ui.shadcn.com/schema.json",
3+
"style": "default",
4+
"rsc": false,
5+
"tsx": true,
6+
"tailwind": {
7+
"config": "libs/ui/src/tailwind.config.js",
8+
"css": "libs/ui/src/tailwind.css",
9+
"baseColor": "slate",
10+
"cssVariables": true,
11+
"prefix": ""
12+
},
13+
"aliases": {
14+
"components": "@humansignal/shad/components",
15+
"ui": "@humansignal/shad/components/ui",
16+
"utils": "@humansignal/shad/utils",
17+
"lib": "@humansignal/shad/lib",
18+
"hooks": "@humansignal/shad/hooks"
19+
},
20+
"iconLibrary": "lucide"
21+
}

web/libs/storybook/.babelrc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"presets": [
3+
[
4+
"@nx/react/babel",
5+
{
6+
"runtime": "automatic",
7+
"useBuiltIns": "usage"
8+
}
9+
]
10+
],
11+
"plugins": []
12+
}

web/libs/storybook/.storybook/main.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import type { StorybookConfig } from "@storybook/react-webpack5";
2+
3+
const config: StorybookConfig = {
4+
core: { builder: "@storybook/builder-webpack5" },
5+
6+
stories: ["../../../libs/**/*.@(mdx|stories.@(js|jsx|ts|tsx))", "../../../apps/**/*.@(mdx|stories.@(js|jsx|ts|tsx))"],
7+
8+
addons: [
9+
"@storybook/addon-essentials",
10+
"@storybook/addon-interactions",
11+
"@nx/react/plugins/storybook",
12+
"@chromatic-com/storybook",
13+
],
14+
15+
webpackFinal(config) {
16+
const rules = config.module?.rules ?? [];
17+
18+
for (const rule of rules) {
19+
if (!rule || typeof rule === "string") continue;
20+
21+
const testString = rule.test?.toString() ?? "";
22+
const isCss = testString.includes("\\.css");
23+
24+
if (isCss) {
25+
rule.exclude = /tailwind\.css/;
26+
}
27+
}
28+
return {
29+
...config,
30+
module: {
31+
...(config.module ?? {}),
32+
rules: [
33+
{
34+
test: /tailwind\.css/,
35+
exclude: /node_modules/,
36+
use: [
37+
"style-loader",
38+
{
39+
loader: "css-loader",
40+
options: {
41+
importLoaders: 1,
42+
},
43+
},
44+
"postcss-loader",
45+
],
46+
},
47+
48+
...(config.module?.rules ?? []),
49+
],
50+
},
51+
};
52+
},
53+
54+
framework: {
55+
name: "@storybook/react-webpack5",
56+
options: {},
57+
},
58+
59+
docs: {},
60+
61+
typescript: {
62+
reactDocgen: "react-docgen-typescript",
63+
},
64+
};
65+
66+
export default config;
67+
68+
// To customize your webpack configuration you can use the webpackFinal field.
69+
// Check https://storybook.js.org/docs/react/builders/webpack#extending-storybooks-webpack-config
70+
// and https://nx.dev/recipes/storybook/custom-builder-configs
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
@import '../../ui/src/tokens/colors';
2+
@import '../../ui/src/tokens/typography';
3+
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import "@humansignal/ui/tailwind.css";
2+
import "./preview.scss";
3+
4+
export const parameters = {};
5+
export const tags = ["autodocs", "autodocs"];
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import type { Meta, StoryObj } from "@storybook/react";
2+
import { Button } from "@humansignal/shad/components/ui/button";
3+
4+
const meta: Meta<typeof Button> = {
5+
component: Button,
6+
render: ({ form, ...args }) => {
7+
return <Button {...args}>Hello</Button>;
8+
},
9+
};
10+
11+
export default meta;
12+
type Story = StoryObj<typeof Button>;
13+
14+
export const Primary: Story = {
15+
args: {
16+
variant: "default",
17+
},
18+
};

web/libs/storybook/jest.config.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/* eslint-disable */
2+
export default {
3+
displayName: "ui",
4+
preset: "../../jest.preset.js",
5+
transform: {
6+
"^(?!.*\\.(js|jsx|ts|tsx|css|json)$)": "@nx/react/plugins/jest",
7+
"^.+\\.[tj]sx?$": ["babel-jest", { presets: ["@nx/react/babel"] }],
8+
},
9+
moduleFileExtensions: ["ts", "tsx", "js", "jsx"],
10+
coverageDirectory: "../../coverage/libs/ui",
11+
};

0 commit comments

Comments
 (0)