mirror of
https://github.com/Kong/insomnia
synced 2024-11-08 06:39:48 +00:00
Project route UI improvements (#6400)
* project tailwind/aria-components * update tests * add failing test * update e2e tests * fix workspace name issue * fix scroll issue * bye test --------- Co-authored-by: Filipe Freire <livrofubia@gmail.com>
This commit is contained in:
parent
7e3e44c50a
commit
81039277af
@ -8,11 +8,11 @@ test('can send requests', async ({ app, page }) => {
|
||||
const responseBody = page.locator('[data-testid="CodeEditor"]:visible', {
|
||||
has: page.locator('.CodeMirror-activeline'),
|
||||
});
|
||||
await page.getByRole('button', { name: 'Create' }).click();
|
||||
await page.getByRole('button', { name: 'Create in project' }).click();
|
||||
const text = await loadFixture('smoke-test-collection.yaml');
|
||||
await app.evaluate(async ({ clipboard }, text) => clipboard.writeText(text), text);
|
||||
|
||||
await page.getByRole('menuitem', { name: 'Import' }).click();
|
||||
await page.getByRole('menuitemradio', { name: 'Import' }).click();
|
||||
await page.getByText('Clipboard').click();
|
||||
await page.getByRole('button', { name: 'Scan' }).click();
|
||||
await page.getByRole('dialog').getByRole('button', { name: 'Import' }).click();
|
||||
|
@ -4,10 +4,10 @@ import { test } from '../../playwright/test';
|
||||
test.describe('Cookie editor', async () => {
|
||||
|
||||
test.beforeEach(async ({ app, page }) => {
|
||||
await page.getByRole('button', { name: 'Create' }).click();
|
||||
await page.getByRole('button', { name: 'Create in project' }).click();
|
||||
const text = await loadFixture('simple.yaml');
|
||||
await app.evaluate(async ({ clipboard }, text) => clipboard.writeText(text), text);
|
||||
await page.getByRole('menuitem', { name: 'Import' }).click();
|
||||
await page.getByRole('menuitemradio', { name: 'Import' }).click();
|
||||
await page.getByText('Clipboard').click();
|
||||
await page.getByRole('button', { name: 'Scan' }).click();
|
||||
await page.getByRole('dialog').getByRole('button', { name: 'Import' }).click();
|
||||
|
@ -7,20 +7,20 @@ test.describe('Dashboard', async () => {
|
||||
test.slow(process.platform === 'darwin' || process.platform === 'win32', 'Slow app start on these platforms');
|
||||
test.describe('Projects', async () => {
|
||||
test('Can create, rename and delete new project', async ({ page }) => {
|
||||
await expect(page.locator('.app')).toContainText('All Files (0)');
|
||||
await page.getByLabel('All Files (0)').click();
|
||||
await expect(page.locator('.app')).not.toContainText('Git Sync');
|
||||
await expect(page.locator('.app')).not.toContainText('Setup Git Sync');
|
||||
|
||||
// Create new project
|
||||
await page.click('[data-testid="CreateProjectButton"]');
|
||||
await page.getByRole('button', { name: 'Create new Project' }).click();
|
||||
await page.locator('text=Create').nth(1).click();
|
||||
|
||||
// Check empty project
|
||||
await expect(page.locator('.app')).toContainText('This is an empty project, to get started create your first resource:');
|
||||
|
||||
// Rename Project
|
||||
await page.click('[data-testid="ProjectDropDown-My-Project"] button');
|
||||
await page.getByRole('menuitem', { name: 'Project Settings' }).click();
|
||||
await page.getByRole('row', { name: 'My Project' }).getByRole('button', { name: 'Project Actions' }).click();
|
||||
await page.getByRole('menuitemradio', { name: 'Settings' }).click();
|
||||
await page.getByPlaceholder('My Project').click();
|
||||
await page.getByPlaceholder('My Project').fill('My Project123');
|
||||
|
||||
@ -32,8 +32,8 @@ test.describe('Dashboard', async () => {
|
||||
await expect(page.locator('.app')).toContainText('My Project123');
|
||||
|
||||
// Delete project
|
||||
await page.click('[data-testid="ProjectDropDown-My-Project123"] button');
|
||||
await page.getByRole('menuitem', { name: 'Project Settings' }).click();
|
||||
await page.getByRole('row', { name: 'My Project' }).getByRole('button', { name: 'Project Actions' }).click();
|
||||
await page.getByRole('menuitemradio', { name: 'Settings' }).click();
|
||||
// Click text=NameActions Delete >> button
|
||||
await page.click('text=NameActions Delete >> button');
|
||||
await page.getByRole('button', { name: 'Click to confirm' }).click();
|
||||
@ -42,27 +42,27 @@ test.describe('Dashboard', async () => {
|
||||
await expect(page.locator('.app')).toContainText('Insomnia');
|
||||
await expect(page.locator('.app')).not.toContainText('My Project123');
|
||||
await expect(page.locator('.app')).toContainText('New Document');
|
||||
await expect(page.locator('.app')).toContainText('All Files (0)');
|
||||
await page.getByLabel('All Files (0)').click();
|
||||
await expect(page.locator('.app')).not.toContainText('Setup Git Sync');
|
||||
});
|
||||
});
|
||||
test.describe('Interactions', async () => { // Not sure about the name here
|
||||
// TODO(INS-2504) - we don't support importing multiple collections at this time
|
||||
test.skip('Can filter through multiple collections', async ({ app, page }) => {
|
||||
await expect(page.locator('.app')).toContainText('All Files (0)');
|
||||
await page.getByLabel('All Files (0)').click();
|
||||
await expect(page.locator('.app')).not.toContainText('Git Sync');
|
||||
await expect(page.locator('.app')).not.toContainText('Setup Git Sync');
|
||||
|
||||
await page.getByRole('button', { name: 'Create' }).click();
|
||||
await page.getByRole('button', { name: 'Create in project' }).click();
|
||||
const text = await loadFixture('multiple-workspaces.yaml');
|
||||
await app.evaluate(async ({ clipboard }, text) => clipboard.writeText(text), text);
|
||||
await page.getByRole('menuitem', { name: 'Import' }).click();
|
||||
await page.getByRole('menuitemradio', { name: 'Import' }).click();
|
||||
await page.getByText('Clipboard').click();
|
||||
await page.getByRole('button', { name: 'Scan' }).click();
|
||||
await page.getByRole('dialog').getByRole('button', { name: 'Import' }).click();
|
||||
await page.getByText('CollectionSmoke testsjust now').click();
|
||||
// Check that 10 new workspaces are imported besides the default one
|
||||
const workspaceCards = page.locator('.card-badge');
|
||||
const workspaceCards = page.getByLabel('Workspaces').getByRole('gridcell');
|
||||
await expect(workspaceCards).toHaveCount(11);
|
||||
await expect(page.locator('.app')).toContainText('New Document');
|
||||
await expect(page.locator('.app')).toContainText('collection 1');
|
||||
@ -87,13 +87,13 @@ test.describe('Dashboard', async () => {
|
||||
});
|
||||
|
||||
test('Can create, rename and delete a document', async ({ page }) => {
|
||||
await expect(page.locator('.app')).toContainText('All Files (0)');
|
||||
await page.getByLabel('All Files (0)').click();
|
||||
await expect(page.locator('.app')).not.toContainText('Git Sync');
|
||||
await expect(page.locator('.app')).not.toContainText('Setup Git Sync');
|
||||
|
||||
// Create new document
|
||||
await page.getByRole('button', { name: 'Create' }).click();
|
||||
await page.getByRole('menuitem', { name: 'Design Document' }).click();
|
||||
await page.getByRole('button', { name: 'Create in project' }).click();
|
||||
await page.getByRole('menuitemradio', { name: 'Design Document' }).click();
|
||||
await page.locator('text=Create').nth(1).click();
|
||||
|
||||
await page.getByTestId('project').click();
|
||||
@ -113,7 +113,7 @@ test.describe('Dashboard', async () => {
|
||||
|
||||
await page.getByTestId('project').click();
|
||||
|
||||
const workspaceCards = page.locator('.card-badge');
|
||||
const workspaceCards = page.getByLabel('Workspaces').getByRole('gridcell');
|
||||
await expect(workspaceCards).toHaveCount(2);
|
||||
|
||||
// Delete document
|
||||
@ -124,13 +124,13 @@ test.describe('Dashboard', async () => {
|
||||
});
|
||||
|
||||
test('Can create, rename and delete a collection', async ({ page }) => {
|
||||
await expect(page.locator('.app')).toContainText('All Files (0)');
|
||||
await page.getByLabel('All Files (0)').click();
|
||||
await expect(page.locator('.app')).not.toContainText('Git Sync');
|
||||
await expect(page.locator('.app')).not.toContainText('Setup Git Sync');
|
||||
|
||||
// Create new collection
|
||||
await page.getByRole('button', { name: 'Create' }).click();
|
||||
await page.getByRole('menuitem', { name: 'Request Collection' }).click();
|
||||
await page.getByRole('button', { name: 'Create in project' }).click();
|
||||
await page.getByRole('menuitemradio', { name: 'Request Collection' }).click();
|
||||
await page.locator('text=Create').nth(1).click();
|
||||
|
||||
await page.getByTestId('project').click();
|
||||
@ -149,7 +149,7 @@ test.describe('Dashboard', async () => {
|
||||
await page.click('[role="dialog"] button:has-text("Duplicate")');
|
||||
|
||||
await page.getByTestId('project').click();
|
||||
const workspaceCards = page.locator('.card-badge');
|
||||
const workspaceCards = page.getByLabel('Workspaces').getByRole('gridcell');
|
||||
await expect(workspaceCards).toHaveCount(2);
|
||||
|
||||
// Delete collection
|
||||
|
@ -6,10 +6,10 @@ import { test } from '../../playwright/test';
|
||||
test.describe('Debug-Sidebar', async () => {
|
||||
test.slow(process.platform === 'darwin' || process.platform === 'win32', 'Slow app start on these platforms');
|
||||
test.beforeEach(async ({ app, page }) => {
|
||||
await page.getByRole('button', { name: 'Create' }).click();
|
||||
await page.getByRole('button', { name: 'Create in project' }).click();
|
||||
const text = await loadFixture('simple.yaml');
|
||||
await app.evaluate(async ({ clipboard }, text) => clipboard.writeText(text), text);
|
||||
await page.getByRole('menuitem', { name: 'Import' }).click();
|
||||
await page.getByRole('menuitemradio', { name: 'Import' }).click();
|
||||
await page.getByText('Clipboard').click();
|
||||
await page.getByRole('button', { name: 'Scan' }).click();
|
||||
await page.getByRole('dialog').getByRole('button', { name: 'Import' }).click();
|
||||
@ -71,8 +71,8 @@ test.describe('Debug-Sidebar', async () => {
|
||||
});
|
||||
|
||||
test('Filter by request name', async ({ page }) => {
|
||||
await page.locator('[placeholder="Filter"]').click();
|
||||
await page.locator('[placeholder="Filter"]').fill('example http');
|
||||
await page.getByLabel('Collection filter').click();
|
||||
await page.getByLabel('Collection filter').fill('example http');
|
||||
await page.getByLabel('Request Collection').getByRole('row', { name: 'example http' }).click();
|
||||
});
|
||||
|
||||
|
@ -0,0 +1,11 @@
|
||||
import { test } from '../../playwright/test';
|
||||
|
||||
test('can name design documents', async ({ page }) => {
|
||||
await page.getByRole('button', { name: ' New Document' }).click();
|
||||
await page.getByPlaceholder('my-spec.yaml').fill('jurassic park');
|
||||
await page.getByPlaceholder('my-spec.yaml').press('Enter');
|
||||
await page.getByLabel('jurassic park').click();
|
||||
await page.getByRole('button', { name: 'jurassic park ' }).press('Escape');
|
||||
await page.getByTestId('project').click();
|
||||
await page.getByLabel('jurassic park').click();
|
||||
});
|
@ -8,10 +8,10 @@ test.describe('Design interactions', async () => {
|
||||
|
||||
test('Unit Test interactions', async ({ app, page }) => {
|
||||
// Setup
|
||||
await page.getByRole('button', { name: 'Create' }).click();
|
||||
await page.getByRole('button', { name: 'Create in project' }).click();
|
||||
const text = await loadFixture('unit-test.yaml');
|
||||
await app.evaluate(async ({ clipboard }, text) => clipboard.writeText(text), text);
|
||||
await page.getByRole('menuitem', { name: 'Import' }).click();
|
||||
await page.getByRole('menuitemradio', { name: 'Import' }).click();
|
||||
await page.getByText('Clipboard').click();
|
||||
await page.getByRole('button', { name: 'Scan' }).click();
|
||||
await page.getByRole('dialog').getByRole('button', { name: 'Import' }).click();
|
||||
|
@ -4,10 +4,10 @@ import { test } from '../../playwright/test';
|
||||
test.describe('Environment Editor', async () => {
|
||||
|
||||
test.beforeEach(async ({ app, page }) => {
|
||||
await page.getByRole('button', { name: 'Create' }).click();
|
||||
await page.getByRole('button', { name: 'Create in project' }).click();
|
||||
const text = await loadFixture('environments.yaml');
|
||||
await app.evaluate(async ({ clipboard }, text) => clipboard.writeText(text), text);
|
||||
await page.getByRole('menuitem', { name: 'Import' }).click();
|
||||
await page.getByRole('menuitemradio', { name: 'Import' }).click();
|
||||
await page.getByText('Clipboard').click();
|
||||
await page.getByRole('button', { name: 'Scan' }).click();
|
||||
await page.getByRole('dialog').getByRole('button', { name: 'Import' }).click();
|
||||
|
@ -52,8 +52,8 @@ test.fixme('Clone Repo with bad values @failing', async ({ page }) => {
|
||||
});
|
||||
|
||||
test.fixme('Clone Gitlab Repo with bad values', async ({ page }) => {
|
||||
await page.getByRole('button', { name: 'Create' }).click();
|
||||
await page.getByRole('menuitem', { name: 'Git Clone' }).click();
|
||||
await page.getByRole('button', { name: 'Create in project' }).click();
|
||||
await page.getByRole('menuitemradio', { name: 'Git Clone' }).click();
|
||||
await page.getByRole('tab', { name: 'Git' }).nth(2).click();
|
||||
|
||||
// Fill in Git Sync details and clone repository
|
||||
|
@ -11,12 +11,12 @@ test.describe('gRPC interactions', () => {
|
||||
let streamMessage: Locator;
|
||||
|
||||
test.beforeEach(async ({ app, page }) => {
|
||||
await page.getByRole('button', { name: 'Create' }).click();
|
||||
await page.getByRole('button', { name: 'Create in project' }).click();
|
||||
|
||||
const text = await loadFixture('grpc.yaml');
|
||||
await app.evaluate(async ({ clipboard }, text) => clipboard.writeText(text), text);
|
||||
|
||||
await page.getByRole('menuitem', { name: 'Import' }).click();
|
||||
await page.getByRole('menuitemradio', { name: 'Import' }).click();
|
||||
await page.getByText('Clipboard').click();
|
||||
await page.getByRole('button', { name: 'Scan' }).click();
|
||||
await page.getByRole('dialog').getByRole('button', { name: 'Import' }).click();
|
||||
|
@ -17,10 +17,10 @@ test('Preferences through keyboard shortcut', async ({ page }) => {
|
||||
|
||||
// Quick reproduction for Kong/insomnia#5664 and INS-2267
|
||||
test('Check filter responses by environment preference', async ({ app, page }) => {
|
||||
await page.getByRole('button', { name: 'Create' }).click();
|
||||
await page.getByRole('button', { name: 'Create in project' }).click();
|
||||
const text = await loadFixture('simple.yaml');
|
||||
await app.evaluate(async ({ clipboard }, text) => clipboard.writeText(text), text);
|
||||
await page.getByRole('menuitem', { name: 'Import' }).click();
|
||||
await page.getByRole('menuitemradio', { name: 'Import' }).click();
|
||||
await page.getByText('Clipboard').click();
|
||||
await page.getByRole('button', { name: 'Scan' }).click();
|
||||
await page.getByRole('dialog').getByRole('button', { name: 'Import' }).click();
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { expect } from '@playwright/test';
|
||||
|
||||
import { loadFixture } from '../../playwright/paths';
|
||||
import { test } from '../../playwright/test';
|
||||
import { test } from '../../playwright/test';;
|
||||
|
||||
test('can send requests', async ({ app, page }) => {
|
||||
test.slow(process.platform === 'darwin' || process.platform === 'win32', 'Slow app start on these platforms');
|
||||
@ -10,17 +10,16 @@ test('can send requests', async ({ app, page }) => {
|
||||
has: page.locator('.CodeMirror-activeline'),
|
||||
});
|
||||
|
||||
await page.getByRole('button', { name: 'Create' }).click();
|
||||
|
||||
const text = await loadFixture('smoke-test-collection.yaml');
|
||||
await app.evaluate(async ({ clipboard }, text) => clipboard.writeText(text), text);
|
||||
|
||||
await page.getByRole('menuitem', { name: 'Import' }).click();
|
||||
await page.getByRole('button', { name: 'Create in project' }).click();
|
||||
await page.getByRole('menuitemradio', { name: 'Import' }).click();
|
||||
await page.getByText('Clipboard').click();
|
||||
await page.getByRole('button', { name: 'Scan' }).click();
|
||||
await page.getByRole('dialog').getByRole('button', { name: 'Import' }).click();
|
||||
|
||||
await page.locator('.card-menu').click();
|
||||
await page.getByRole('button', { name: 'Workspace actions menu button' }).click();
|
||||
await page.getByRole('menuitem', { name: 'Export' }).click();
|
||||
await page.getByRole('dialog').getByRole('checkbox').nth(1).uncheck();
|
||||
await page.getByRole('button', { name: 'Export' }).click();
|
||||
@ -88,12 +87,12 @@ test('can send requests', async ({ app, page }) => {
|
||||
// This feature is unsafe to place beside other tests, cancelling a request can cause network code to block
|
||||
// related to https://linear.app/insomnia/issue/INS-973
|
||||
test('can cancel requests', async ({ app, page }) => {
|
||||
await page.getByRole('button', { name: 'Create' }).click();
|
||||
await page.getByRole('button', { name: 'Create in project' }).click();
|
||||
|
||||
const text = await loadFixture('smoke-test-collection.yaml');
|
||||
await app.evaluate(async ({ clipboard }, text) => clipboard.writeText(text), text);
|
||||
|
||||
await page.getByRole('menuitem', { name: 'Import' }).click();
|
||||
await page.getByRole('menuitemradio', { name: 'Import' }).click();
|
||||
await page.getByText('Clipboard').click();
|
||||
await page.getByRole('button', { name: 'Scan' }).click();
|
||||
await page.getByRole('dialog').getByRole('button', { name: 'Import' }).click();
|
||||
|
@ -6,14 +6,14 @@ import { test } from '../../playwright/test';
|
||||
test('can render schema and send GraphQL requests', async ({ app, page }) => {
|
||||
test.slow(process.platform === 'darwin' || process.platform === 'win32', 'Slow app start on these platforms');
|
||||
|
||||
await page.getByRole('button', { name: 'Create' }).click();
|
||||
await page.getByRole('button', { name: 'Create in project' }).click();
|
||||
|
||||
// Copy the collection with the graphql query to clipboard
|
||||
const text = await loadFixture('graphql.yaml');
|
||||
await app.evaluate(async ({ clipboard }, text) => clipboard.writeText(text), text);
|
||||
|
||||
// Import from clipboard
|
||||
await page.getByRole('menuitem', { name: 'Import' }).click();
|
||||
await page.getByRole('menuitemradio', { name: 'Import' }).click();
|
||||
await page.getByText('Clipboard').click();
|
||||
await page.getByRole('button', { name: 'Scan' }).click();
|
||||
await page.getByRole('dialog').getByRole('button', { name: 'Import' }).click();
|
||||
@ -46,11 +46,11 @@ test('can render schema and send GraphQL requests', async ({ app, page }) => {
|
||||
test('can send GraphQL requests after editing and prettifying query', async ({ app, page }) => {
|
||||
test.slow(process.platform === 'darwin' || process.platform === 'win32', 'Slow app start on these platforms');
|
||||
|
||||
await page.getByRole('button', { name: 'Create' }).click();
|
||||
await page.getByRole('button', { name: 'Create in project' }).click();
|
||||
|
||||
const text = await loadFixture('graphql.yaml');
|
||||
await app.evaluate(async ({ clipboard }, text) => clipboard.writeText(text), text);
|
||||
await page.getByRole('menuitem', { name: 'Import' }).click();
|
||||
await page.getByRole('menuitemradio', { name: 'Import' }).click();
|
||||
await page.getByText('Clipboard').click();
|
||||
await page.getByRole('button', { name: 'Scan' }).click();
|
||||
await page.getByRole('dialog').getByRole('button', { name: 'Import' }).click();
|
||||
|
@ -10,12 +10,12 @@ test('can send gRPC requests with reflection', async ({ app, page }) => {
|
||||
has: page.locator('.CodeMirror-activeline'),
|
||||
});
|
||||
|
||||
await page.getByRole('button', { name: 'Create' }).click();
|
||||
await page.getByRole('button', { name: 'Create in project' }).click();
|
||||
|
||||
const text = await loadFixture('grpc.yaml');
|
||||
await app.evaluate(async ({ clipboard }, text) => clipboard.writeText(text), text);
|
||||
|
||||
await page.getByRole('menuitem', { name: 'Import' }).click();
|
||||
await page.getByRole('menuitemradio', { name: 'Import' }).click();
|
||||
await page.getByText('Clipboard').click();
|
||||
await page.getByRole('button', { name: 'Scan' }).click();
|
||||
await page.getByRole('dialog').getByRole('button', { name: 'Import' }).click();
|
||||
|
@ -17,12 +17,12 @@ test('can make oauth2 requests', async ({ app, page }) => {
|
||||
});
|
||||
|
||||
const projectView = page.locator('#wrapper');
|
||||
await projectView.getByRole('button', { name: 'Create' }).click();
|
||||
await projectView.getByRole('button', { name: 'Create in project' }).click();
|
||||
|
||||
const text = await loadFixture('oauth.yaml');
|
||||
await app.evaluate(async ({ clipboard }, text) => clipboard.writeText(text), text);
|
||||
|
||||
await page.getByRole('menuitem', { name: 'Import' }).click();
|
||||
await page.getByRole('menuitemradio', { name: 'Import' }).click();
|
||||
await page.getByText('Clipboard').click();
|
||||
await page.getByRole('button', { name: 'Scan' }).click();
|
||||
await page.getByRole('dialog').getByRole('button', { name: 'Import' }).click();
|
||||
|
@ -10,12 +10,12 @@ test('can make websocket connection', async ({ app, page }) => {
|
||||
has: page.locator('.CodeMirror-activeline'),
|
||||
});
|
||||
|
||||
await page.getByRole('button', { name: 'Create' }).click();
|
||||
await page.getByRole('button', { name: 'Create in project' }).click();
|
||||
|
||||
const text = await loadFixture('websockets.yaml');
|
||||
await app.evaluate(async ({ clipboard }, text) => clipboard.writeText(text), text);
|
||||
|
||||
await page.getByRole('menuitem', { name: 'Import' }).click();
|
||||
await page.getByRole('menuitemradio', { name: 'Import' }).click();
|
||||
await page.getByText('Clipboard').click();
|
||||
await page.getByRole('button', { name: 'Scan' }).click();
|
||||
await page.getByRole('dialog').getByRole('button', { name: 'Import' }).click();
|
||||
|
@ -1,28 +0,0 @@
|
||||
import { describe, expect, it } from '@jest/globals';
|
||||
|
||||
import { getVersionDisplayment } from '../workspace-card';
|
||||
|
||||
describe('getVersionDisplayment', () => {
|
||||
it('returns null, undefined, and empty string as-is', () => {
|
||||
expect(getVersionDisplayment(null)).toEqual(null);
|
||||
expect(getVersionDisplayment(undefined)).toEqual(undefined);
|
||||
expect(getVersionDisplayment('')).toEqual('');
|
||||
});
|
||||
|
||||
it('returns numbers as strings', () => {
|
||||
expect(getVersionDisplayment(0)).toEqual('v0'); // important to make sure we handle, since `0` is falsey
|
||||
expect(getVersionDisplayment(1)).toEqual('v1');
|
||||
expect(getVersionDisplayment(1.1)).toEqual('v1.1');
|
||||
});
|
||||
|
||||
it('does not add a `v` if the string starts with one', () => {
|
||||
expect(getVersionDisplayment('v1')).toEqual('v1');
|
||||
expect(getVersionDisplayment('victor')).toEqual('victor');
|
||||
});
|
||||
|
||||
it("adds a `v` to all strings that don't start with a v", () => {
|
||||
expect(getVersionDisplayment('1')).toEqual('v1');
|
||||
expect(getVersionDisplayment('1.0.0')).toEqual('v1.0.0');
|
||||
expect(getVersionDisplayment('alpha1')).toEqual('valpha1'); // yes, we know this is non-ideal, see INS-1320.
|
||||
});
|
||||
});
|
@ -1,297 +0,0 @@
|
||||
import React, { FC, ReactNode, useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { IconEnum, SvgIcon } from './svg-icon';
|
||||
|
||||
const StyledCard = styled.div({
|
||||
'&&': {
|
||||
transition: 'all 0.1s ease-out',
|
||||
},
|
||||
height: '196px',
|
||||
width: '204px',
|
||||
border: '1px solid var(--hl-sm)',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexGrow: '0',
|
||||
flexShrink: '0',
|
||||
color: 'var(--font-dark)',
|
||||
borderRadius: 'var(--radius-md)',
|
||||
|
||||
'&:hover': {
|
||||
borderColor: 'var(--color-surprise)',
|
||||
boxShadow: 'var(--padding-sm) var(--padding-sm) calc(var(--padding-xl) * 1.5) calc(0px - var(--padding-xl)) rgba(0, 0, 0, 0.2)',
|
||||
cursor: 'pointer',
|
||||
'.title': {
|
||||
color: 'var(--color-surprise)',
|
||||
},
|
||||
},
|
||||
|
||||
'&.selected': {
|
||||
backgroundColor: 'rgba(var(--color-surprise-rgb), 0.05)',
|
||||
borderColor: 'var(--color-surprise)',
|
||||
'.title': {
|
||||
color: 'var(--color-surprise)',
|
||||
},
|
||||
cursor: 'default',
|
||||
'&:hover': {
|
||||
boxShadow: 'none',
|
||||
},
|
||||
},
|
||||
|
||||
'&.deselected': {
|
||||
backgroundColor: 'transparent',
|
||||
border: '1px solid var(--hl-sm)',
|
||||
cursor: 'default',
|
||||
'&:hover': {
|
||||
borderColor: 'var(--color-surprise)',
|
||||
boxShadow: '3px 3px 20px -10px rgba(0, 0, 0, 0.2)',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const CardHeader = styled.div({
|
||||
textAlign: 'left',
|
||||
padding: 'var(--padding-md) var(--padding-xs) 0 var(--padding-xs)',
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
|
||||
'.header-item': {
|
||||
fontSize: 'var(--font-size-xs)',
|
||||
padding: 'var(--padding-xs)',
|
||||
},
|
||||
|
||||
'.card-badge': {
|
||||
marginLeft: 'var(--padding-sm)',
|
||||
backgroundColor: 'var(--hl-xs)',
|
||||
borderRadius: 'var(--radius-sm)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
padding: 0,
|
||||
overflow: 'hidden',
|
||||
|
||||
'&.card-icon': {
|
||||
padding: 0,
|
||||
},
|
||||
|
||||
'&:empty': {
|
||||
visibility: 'hidden',
|
||||
},
|
||||
},
|
||||
|
||||
'.card-menu': {
|
||||
margin: 'calc(-1 * var(--padding-sm))',
|
||||
marginRight: 'var(--padding-xs)',
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
fontWeight: '900',
|
||||
fontSize: 'var(--font-size-lg)',
|
||||
color: 'var(--font-color)',
|
||||
|
||||
button: {
|
||||
padding: 'var(--padding-xs) var(--padding-sm)',
|
||||
|
||||
'&:hover': {
|
||||
backgroundColor: 'var(--hl-xxs)',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
'.card-checkbox-label': {
|
||||
display: 'block',
|
||||
position: 'relative',
|
||||
margin: 'auto',
|
||||
cursor: 'default',
|
||||
fontSize: 'var(--font-size-xl)',
|
||||
lineHeight: 'var(--font-size-xl)',
|
||||
height: 'var(--font-size-xl)',
|
||||
width: 'var(--font-size-xl)',
|
||||
clear: 'both',
|
||||
|
||||
'.card-checkbox': {
|
||||
position: 'absolute',
|
||||
top: '0',
|
||||
left: '0',
|
||||
height: 'var(--font-size-xl)',
|
||||
width: 'var(--font-size-xl)',
|
||||
backgroundColor: 'rgba(var(--color-surprise-rgb), 0.1)',
|
||||
borderRadius: 'var(--radius-md)',
|
||||
border: '2px solid var(--color-surprise)',
|
||||
|
||||
'&::after': {
|
||||
position: 'absolute',
|
||||
content: '""',
|
||||
height: '0',
|
||||
width: '0',
|
||||
borderRadius: 'var(--radius-md)',
|
||||
border: 'solid var(--color-font-info)',
|
||||
borderWidth: '0 var(--padding-sm) var(--padding-sm) 0',
|
||||
transform: 'rotate(0deg) scale(0)',
|
||||
opacity: '1',
|
||||
},
|
||||
},
|
||||
|
||||
input :{
|
||||
position: 'absolute',
|
||||
opacity: '0',
|
||||
cursor: 'default',
|
||||
|
||||
'&:checked ~ .card-checkbox': {
|
||||
backgroundColor: 'var(--color-surprise)',
|
||||
borderRadius: 'var(--radius-md)',
|
||||
transform: 'rotate(0deg) scale(1)',
|
||||
opacity: '1',
|
||||
|
||||
'&::after': {
|
||||
transform: 'rotate(45deg) scale(1)',
|
||||
opacity: '1',
|
||||
left: '0.375rem',
|
||||
top: '0.125rem',
|
||||
width: '0.3125rem',
|
||||
height: '0.625rem',
|
||||
border: 'solid var(--color-bg)',
|
||||
borderWidth: '0 2px 2px 0',
|
||||
backgroundColor: 'transparent',
|
||||
borderRadius: '0',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const CardBody = styled.div({
|
||||
justifyContent: 'normal',
|
||||
fontWeight: '400',
|
||||
color: 'var(--font-color)',
|
||||
marginTop: 'var(--padding-md)',
|
||||
paddingLeft: 'var(--padding-md)',
|
||||
overflowY: 'auto',
|
||||
|
||||
'.title': {
|
||||
fontSize: 'var(--font-size-md)',
|
||||
paddingRight: 'var(--padding-md)',
|
||||
overflowX: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
},
|
||||
|
||||
'.version': {
|
||||
fontSize: 'var(--font-size-xs)',
|
||||
paddingTop: 'var(--padding-xs)',
|
||||
},
|
||||
});
|
||||
|
||||
const CardFooter = styled.div({
|
||||
marginTop: 'auto',
|
||||
paddingLeft: 'var(--padding-md)',
|
||||
paddingTop: 'var(--padding-sm)',
|
||||
paddingBottom: 'var(--padding-sm)',
|
||||
color: 'var(--hl-xl)',
|
||||
|
||||
'span': {
|
||||
display: 'flex',
|
||||
justifyContent: 'left',
|
||||
flexDirection: 'row',
|
||||
marginBottom: 'var(--padding-xs)',
|
||||
svg: {
|
||||
width: '1em',
|
||||
height: '1em',
|
||||
},
|
||||
},
|
||||
|
||||
'.icoLabel': {
|
||||
paddingLeft: 'var(--padding-xs)',
|
||||
fontSize: 'var(--font-size-sm)',
|
||||
'*': {
|
||||
display: 'inline',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export interface CardProps {
|
||||
docBranch?: ReactNode;
|
||||
docLog?: ReactNode;
|
||||
docMenu?: ReactNode;
|
||||
docTitle?: ReactNode;
|
||||
docVersion?: ReactNode;
|
||||
tagLabel: ReactNode;
|
||||
docFormat?: ReactNode;
|
||||
onChange?: (event: React.SyntheticEvent<HTMLInputElement>) => any;
|
||||
onClick?: (event: React.SyntheticEvent<HTMLDivElement>) => any;
|
||||
selectable?: boolean;
|
||||
}
|
||||
|
||||
export const Card: FC<CardProps> = props => {
|
||||
const [state, setState] = useState({
|
||||
selected: false,
|
||||
selectable: false,
|
||||
});
|
||||
|
||||
const {
|
||||
tagLabel,
|
||||
docTitle,
|
||||
docVersion,
|
||||
docBranch,
|
||||
docLog,
|
||||
docMenu,
|
||||
docFormat,
|
||||
selectable,
|
||||
onClick,
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<StyledCard className={state.selected ? 'selected' : 'deselected'} onClick={onClick}>
|
||||
<CardHeader>
|
||||
<div className="header-item card-badge">{tagLabel}</div>
|
||||
{selectable ? (
|
||||
<div className="header-item card-menu">
|
||||
<label className="card-checkbox-label">
|
||||
<input
|
||||
type="checkbox"
|
||||
onChange={event => {
|
||||
setState(state => ({
|
||||
...state,
|
||||
selected: !state.selected,
|
||||
}));
|
||||
|
||||
props.onChange?.(event);
|
||||
}}
|
||||
/>
|
||||
<span className="card-checkbox" />
|
||||
</label>
|
||||
</div>
|
||||
) : (
|
||||
<div className="header-item card-menu">{docMenu}</div>
|
||||
)}
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
{docTitle && (
|
||||
<div className="title">
|
||||
<strong>{docTitle}</strong>
|
||||
</div>
|
||||
)}
|
||||
{docVersion && <div className="version">{docVersion}</div>}
|
||||
</CardBody>
|
||||
<CardFooter>
|
||||
{docFormat && (
|
||||
<span>
|
||||
<SvgIcon icon={IconEnum.file} />
|
||||
<div className="icoLabel">{docFormat}</div>
|
||||
</span>
|
||||
)}
|
||||
{docBranch && (
|
||||
<span>
|
||||
<SvgIcon icon={IconEnum.gitBranch} />
|
||||
<div className="icoLabel">{docBranch}</div>
|
||||
</span>
|
||||
)}
|
||||
{docLog && (
|
||||
<span>
|
||||
<SvgIcon icon={IconEnum.clock} />
|
||||
<div className="icoLabel">{docLog}</div>
|
||||
</span>
|
||||
)}
|
||||
</CardFooter>
|
||||
</StyledCard>
|
||||
);
|
||||
};
|
@ -1,12 +1,18 @@
|
||||
import { IconName } from '@fortawesome/fontawesome-svg-core';
|
||||
import React, { FC, Fragment, useState } from 'react';
|
||||
import {
|
||||
Button,
|
||||
Item,
|
||||
Menu,
|
||||
MenuTrigger,
|
||||
Popover,
|
||||
} from 'react-aria-components';
|
||||
import { useFetcher } from 'react-router-dom';
|
||||
|
||||
import { toKebabCase } from '../../../common/misc';
|
||||
import { strings } from '../../../common/strings';
|
||||
import {
|
||||
Project,
|
||||
} from '../../../models/project';
|
||||
import { Dropdown, DropdownButton, DropdownItem, ItemContent } from '../base/dropdown';
|
||||
import { Icon } from '../icon';
|
||||
import ProjectSettingsModal from '../modals/project-settings-modal';
|
||||
|
||||
interface Props {
|
||||
@ -15,46 +21,72 @@ interface Props {
|
||||
}
|
||||
|
||||
export const ProjectDropdown: FC<Props> = ({ project, organizationId }) => {
|
||||
const [isSettingsModalOpen, setIsSettingsModalOpen] = useState(false);
|
||||
const [isProjectSettingsModalOpen, setIsProjectSettingsModalOpen] =
|
||||
useState(false);
|
||||
const deleteProjectFetcher = useFetcher();
|
||||
|
||||
const projectActionList: {
|
||||
id: string;
|
||||
name: string;
|
||||
icon: IconName;
|
||||
action: (projectId: string) => void;
|
||||
}[] = [
|
||||
{
|
||||
id: 'settings',
|
||||
name: 'Settings',
|
||||
icon: 'gear',
|
||||
action: () => setIsProjectSettingsModalOpen(true),
|
||||
},
|
||||
{
|
||||
id: 'delete',
|
||||
name: 'Delete',
|
||||
icon: 'trash',
|
||||
action: projectId =>
|
||||
deleteProjectFetcher.submit(
|
||||
{},
|
||||
{
|
||||
method: 'post',
|
||||
action: `/organization/${organizationId}/project/${projectId}/delete`,
|
||||
}
|
||||
),
|
||||
},
|
||||
];
|
||||
return (
|
||||
<Fragment>
|
||||
<Dropdown
|
||||
aria-label='Project Dropdown'
|
||||
dataTestId={toKebabCase(`ProjectDropDown-${project.name}`)}
|
||||
triggerButton={
|
||||
<DropdownButton className="row" title={project.name}>
|
||||
<i className="fa fa-ellipsis space-left" />
|
||||
</DropdownButton>
|
||||
}
|
||||
>
|
||||
<DropdownItem aria-label={`${strings.project.singular} Settings`}>
|
||||
<ItemContent
|
||||
icon="gear"
|
||||
style={{ gap: 'var(--padding-sm)' }}
|
||||
iconStyle={{ width: 'unset', fill: 'var(--hl)' }}
|
||||
label={`${strings.project.singular} Settings`}
|
||||
onClick={() => setIsSettingsModalOpen(true)}
|
||||
/>
|
||||
</DropdownItem>
|
||||
<DropdownItem aria-label='Delete'>
|
||||
<ItemContent
|
||||
icon="trash-o"
|
||||
label="Delete"
|
||||
className="danger"
|
||||
withPrompt
|
||||
onClick={() =>
|
||||
deleteProjectFetcher.submit(
|
||||
{},
|
||||
{ method: 'post', action: `/organization/${organizationId}/project/${project._id}/delete` }
|
||||
)
|
||||
}
|
||||
/>
|
||||
</DropdownItem>
|
||||
</Dropdown>
|
||||
{isSettingsModalOpen && (
|
||||
<MenuTrigger>
|
||||
<Button
|
||||
aria-label="Project Actions"
|
||||
className="opacity-0 items-center hover:opacity-100 focus:opacity-100 data-[pressed]:opacity-100 flex group-focus:opacity-100 group-hover:opacity-100 justify-center h-6 aspect-square aria-pressed:bg-[--hl-sm] rounded-sm text-[--color-font] hover:bg-[--hl-xs] focus:ring-inset ring-1 ring-transparent focus:ring-[--hl-md] transition-all text-sm"
|
||||
>
|
||||
<Icon icon="caret-down" />
|
||||
</Button>
|
||||
<Popover className="min-w-max">
|
||||
<Menu
|
||||
aria-label="Project Actions Menu"
|
||||
selectionMode="single"
|
||||
onAction={key => {
|
||||
projectActionList.find(({ id }) => key === id)?.action(project._id);
|
||||
}}
|
||||
items={projectActionList}
|
||||
className="border select-none text-sm min-w-max border-solid border-[--hl-sm] shadow-lg bg-[--color-bg] py-2 rounded-md overflow-y-auto max-h-[85vh] focus:outline-none"
|
||||
>
|
||||
{item => (
|
||||
<Item
|
||||
key={item.id}
|
||||
id={item.id}
|
||||
className="flex gap-2 px-[--padding-md] aria-selected:font-bold items-center text-[--color-font] h-[--line-height-xs] w-full text-md whitespace-nowrap bg-transparent hover:bg-[--hl-sm] disabled:cursor-not-allowed focus:bg-[--hl-xs] focus:outline-none transition-colors"
|
||||
aria-label={item.name}
|
||||
>
|
||||
<Icon icon={item.icon} />
|
||||
<span>{item.name}</span>
|
||||
</Item>
|
||||
)}
|
||||
</Menu>
|
||||
</Popover>
|
||||
</MenuTrigger>
|
||||
{isProjectSettingsModalOpen && (
|
||||
<ProjectSettingsModal
|
||||
onHide={() => setIsSettingsModalOpen(false)}
|
||||
onHide={() => setIsProjectSettingsModalOpen(false)}
|
||||
project={project}
|
||||
/>
|
||||
)}
|
||||
|
@ -104,7 +104,7 @@ export const WorkspaceCardDropdown: FC<Props> = props => {
|
||||
aria-label='Workspace Actions Dropdown'
|
||||
onOpen={refresh}
|
||||
triggerButton={
|
||||
<DropdownButton>
|
||||
<DropdownButton aria-label='Workspace actions menu button' className="px-4 py-1 flex flex-1 items-center justify-center gap-2 aria-pressed:bg-[--hl-sm] rounded-sm text-[--color-font] hover:bg-[--hl-xs] focus:ring-inset ring-1 ring-transparent focus:ring-[--hl-md] transition-all text-sm">
|
||||
<SvgIcon icon="ellipsis" />
|
||||
</DropdownButton>
|
||||
}
|
||||
|
@ -1,192 +0,0 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import { FC } from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import {
|
||||
ACTIVITY_DEBUG,
|
||||
ACTIVITY_SPEC,
|
||||
GlobalActivity,
|
||||
} from '../../common/constants';
|
||||
import { fuzzyMatchAll } from '../../common/misc';
|
||||
import { strings } from '../../common/strings';
|
||||
import { Project } from '../../models/project';
|
||||
import { isDesign } from '../../models/workspace';
|
||||
import { WorkspaceWithMetadata } from '../routes/project';
|
||||
import { Highlight } from './base/highlight';
|
||||
import { Card } from './card';
|
||||
import { WorkspaceCardDropdown } from './dropdowns/workspace-card-dropdown';
|
||||
import { TimeFromNow } from './time-from-now';
|
||||
|
||||
const Label = styled.div({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
overflow: 'hidden',
|
||||
gap: 'var(--padding-sm)',
|
||||
height: '1.5rem',
|
||||
paddingRight: 'var(--padding-sm)',
|
||||
});
|
||||
|
||||
const LabelIcon = styled.div({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
padding: '0.2rem',
|
||||
height: '1rem',
|
||||
});
|
||||
|
||||
export interface WorkspaceCardProps {
|
||||
workspaceWithMetadata: WorkspaceWithMetadata;
|
||||
filter: string;
|
||||
activeProject: Project;
|
||||
onSelect: (workspaceId: string, activity: GlobalActivity) => void;
|
||||
projects: Project[];
|
||||
}
|
||||
|
||||
/** note: numbers are not technically valid (and, indeed, we throw a lint error), but we need to handle this case otherwise a user will not be able to import a spec with a malformed version and even _see_ that it's got the error. */
|
||||
export const getVersionDisplayment = (version?: string | number | null) => {
|
||||
if (version === null || version === undefined || version === '') {
|
||||
return version;
|
||||
}
|
||||
|
||||
if (typeof version === 'number') {
|
||||
console.warn(
|
||||
`OpenAPI documents must not use number data types for $.info.version, found ${version}`
|
||||
);
|
||||
version = String(version);
|
||||
} else if (typeof version !== 'string') {
|
||||
console.error('unable to parse spec version');
|
||||
return '';
|
||||
}
|
||||
|
||||
if (!version.startsWith('v')) {
|
||||
return `v${version}`;
|
||||
}
|
||||
|
||||
return version;
|
||||
};
|
||||
|
||||
export const WorkspaceCard: FC<WorkspaceCardProps> = ({
|
||||
workspaceWithMetadata,
|
||||
filter,
|
||||
activeProject,
|
||||
projects,
|
||||
onSelect,
|
||||
}) => {
|
||||
const {
|
||||
apiSpec,
|
||||
lastActiveBranch,
|
||||
lastModifiedTimestamp,
|
||||
workspace,
|
||||
lastCommitTime,
|
||||
modifiedLocally,
|
||||
lastCommitAuthor,
|
||||
spec,
|
||||
specFormat,
|
||||
specFormatVersion,
|
||||
hasUnsavedChanges,
|
||||
workspaceMeta,
|
||||
clientCertificates,
|
||||
caCertificate,
|
||||
} = workspaceWithMetadata;
|
||||
let branch = lastActiveBranch;
|
||||
|
||||
let log = <TimeFromNow timestamp={lastModifiedTimestamp} />;
|
||||
|
||||
if (hasUnsavedChanges) {
|
||||
// Show locally unsaved changes for spec
|
||||
// NOTE: this doesn't work for non-spec workspaces
|
||||
branch = lastActiveBranch + '*';
|
||||
if (modifiedLocally) {
|
||||
log = (
|
||||
<Fragment>
|
||||
<TimeFromNow className="text-danger" timestamp={modifiedLocally} />{' '}
|
||||
(unsaved)
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
} else if (lastCommitTime) {
|
||||
// Show last commit time and author
|
||||
log = (
|
||||
<Fragment>
|
||||
<TimeFromNow timestamp={lastCommitTime} />{' '}
|
||||
{lastCommitAuthor && `by ${lastCommitAuthor}`}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
const version = getVersionDisplayment(spec?.info?.version);
|
||||
let label: string = strings.collection.singular;
|
||||
let format = '';
|
||||
let labelIcon = <i className="fa fa-bars" />;
|
||||
let defaultActivity = ACTIVITY_DEBUG;
|
||||
let title = workspace.name;
|
||||
|
||||
if (isDesign(workspace)) {
|
||||
label = strings.document.singular;
|
||||
labelIcon = <i className="fa fa-file-o" />;
|
||||
|
||||
if (specFormat === 'openapi') {
|
||||
format = `OpenAPI ${specFormatVersion}`;
|
||||
} else if (specFormat === 'swagger') {
|
||||
// NOTE: This is not a typo, we're labeling Swagger as OpenAPI also
|
||||
format = `OpenAPI ${specFormatVersion}`;
|
||||
}
|
||||
|
||||
defaultActivity = ACTIVITY_SPEC;
|
||||
title = apiSpec?.fileName || title;
|
||||
}
|
||||
|
||||
// Filter the card by multiple different properties
|
||||
const matchResults = fuzzyMatchAll(
|
||||
filter,
|
||||
[title, label, branch || '', version || ''],
|
||||
{
|
||||
splitSpace: true,
|
||||
loose: true,
|
||||
}
|
||||
);
|
||||
|
||||
// Return null if we don't match the filter
|
||||
if (filter && !matchResults) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Card
|
||||
docBranch={
|
||||
branch ? <Highlight search={filter} text={branch} /> : undefined
|
||||
}
|
||||
docTitle={title ? <Highlight search={filter} text={title} /> : undefined}
|
||||
docVersion={
|
||||
version ? <Highlight search={filter} text={version} /> : undefined
|
||||
}
|
||||
tagLabel={
|
||||
label ? (
|
||||
<Label>
|
||||
<LabelIcon
|
||||
style={{
|
||||
color: isDesign(workspace) ? 'var(--color-font-info)' : 'var(--color-font-surprise)',
|
||||
backgroundColor: isDesign(workspace) ? 'var(--color-info)' : 'var(--color-surprise)',
|
||||
}}
|
||||
>{labelIcon}</LabelIcon>
|
||||
<Highlight search={filter} text={label} />
|
||||
</Label>
|
||||
) : undefined
|
||||
}
|
||||
docLog={log}
|
||||
docMenu={
|
||||
<WorkspaceCardDropdown
|
||||
apiSpec={apiSpec}
|
||||
workspace={workspace}
|
||||
workspaceMeta={workspaceMeta}
|
||||
project={activeProject}
|
||||
projects={projects}
|
||||
clientCertificates={clientCertificates}
|
||||
caCertificate={caCertificate}
|
||||
/>
|
||||
}
|
||||
docFormat={format}
|
||||
onClick={() => onSelect(workspace._id, defaultActivity)}
|
||||
/>
|
||||
);
|
||||
};
|
@ -107,6 +107,8 @@ export const createNewWorkspaceAction: ActionFunction = async ({
|
||||
parentId: projectId,
|
||||
});
|
||||
|
||||
console.log({ workspace, name });
|
||||
|
||||
if (scope === 'design') {
|
||||
await models.apiSpec.getOrCreateForParentId(workspace._id);
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -428,14 +428,14 @@ const Root = () => {
|
||||
)}
|
||||
</div>
|
||||
</header>
|
||||
<div className="[grid-area:Navbar]">
|
||||
<div className="[grid-area:Navbar] overflow-hidden">
|
||||
<nav className="flex flex-col items-center place-content-stretch gap-[--padding-md] w-full h-full overflow-y-auto py-[--padding-md]">
|
||||
{organizations.map(organization => (
|
||||
<TooltipTrigger key={organization._id}>
|
||||
<Link>
|
||||
<NavLink
|
||||
className={({ isActive }) =>
|
||||
`select-none text-[--color-font-surprise] hover:no-underline transition-all duration-150 bg-gradient-to-br box-border from-[#4000BF] to-[#154B62] p-[--padding-sm] font-bold outline-[3px] rounded-md w-[28px] h-[28px] flex items-center justify-center active:outline overflow-hidden outline-offset-[3px] outline ${
|
||||
`select-none text-[--color-font-surprise] flex-shrink-0 hover:no-underline transition-all duration-150 bg-gradient-to-br box-border from-[#4000BF] to-[#154B62] p-[--padding-sm] font-bold outline-[3px] rounded-md w-[28px] h-[28px] flex items-center justify-center active:outline overflow-hidden outline-offset-[3px] outline ${
|
||||
isActive
|
||||
? 'outline-[--color-font]'
|
||||
: 'outline-transparent focus:outline-[--hl-md] hover:outline-[--hl-md]'
|
||||
|
Loading…
Reference in New Issue
Block a user