diff --git a/.eslintrc.js b/.eslintrc.js index 964c304f3b43c..5f71d84aebdd8 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -42,7 +42,7 @@ module.exports = { }, ecmaVersion: 2018, sourceType: 'module', - project: 'tsconfig.json' + project: 'tsconfig.json', }, plugins: [ 'prettier', @@ -124,6 +124,11 @@ module.exports = { importNames: ['Tooltip'], message: 'Please use Tooltip from @posthog/lemon-ui instead.', }, + { + name: 'antd', + importNames: ['Alert'], + message: 'Please use LemonBanner from @posthog/lemon-ui instead.', + }, ], }, ], @@ -243,6 +248,10 @@ module.exports = { element: 'a', message: 'use instead', }, + { + element: 'Alert', + message: 'use instead', + }, ], }, ], @@ -266,7 +275,7 @@ module.exports = { rules: { // The below complains needlessly about expect(api.createInvite).toHaveBeenCalledWith(...) '@typescript-eslint/unbound-method': 'off', - } + }, }, { files: ['*Type.ts', '*Type.tsx'], // Kea typegen output diff --git a/.github/workflows/ci-frontend.yml b/.github/workflows/ci-frontend.yml index 1aa94fa6a659a..2ea70218bd608 100644 --- a/.github/workflows/ci-frontend.yml +++ b/.github/workflows/ci-frontend.yml @@ -33,8 +33,8 @@ jobs: frontend: # Avoid running frontend tests for irrelevant changes # NOTE: we are at risk of missing a dependency here. - - 'bin/*' - - frontend/* + - 'bin/**' + - 'frontend/**' # Make sure we run if someone is explicitly change the workflow - .github/workflows/ci-frontend.yml # various JS config files diff --git a/.stylelintrc.js b/.stylelintrc.js index 5df820f88d8fb..19bbc999f0f51 100644 --- a/.stylelintrc.js +++ b/.stylelintrc.js @@ -10,6 +10,7 @@ module.exports = { // CSS Color Module Level 3 says currentColor, Level 4 candidate says currentcolor // Sticking to Level 3 for now camelCaseSvgKeywords: true, + ignoreKeywords: ['BlinkMacSystemFont'], // BlinkMacSystemFont MUST have this particular casing }, ], // Sadly Safari only started supporting the range syntax of media queries in 2023, so let's switch to that diff --git a/cypress/e2e/featureFlags.cy.ts b/cypress/e2e/featureFlags.cy.ts index 9045a51a4f485..bf37822321ad1 100644 --- a/cypress/e2e/featureFlags.cy.ts +++ b/cypress/e2e/featureFlags.cy.ts @@ -48,6 +48,7 @@ describe('Feature Flags', () => { cy.get('[data-attr=save-feature-flag]').first().click() // after save there should be a delete button + cy.get('[data-attr="more-button"]').click() cy.get('button[data-attr="delete-feature-flag"]').should('have.text', 'Delete feature flag') // make sure the data is there as expected after a page reload! @@ -83,11 +84,13 @@ describe('Feature Flags', () => { cy.get('[data-attr=save-feature-flag]').first().click() // after save there should be a delete button + cy.get('[data-attr="more-button"]').click() cy.get('button[data-attr="delete-feature-flag"]').should('have.text', 'Delete feature flag') cy.clickNavMenu('featureflags') cy.get('[data-attr=feature-flag-table]').should('contain', name) cy.get(`[data-row-key=${name}]`).contains(name).click() + cy.get('[data-attr="more-button"]').click() cy.get('[data-attr=delete-feature-flag]').click() cy.get('.Toastify').contains('Undo').should('be.visible') }) diff --git a/ee/api/feature_flag_role_access.py b/ee/api/feature_flag_role_access.py index d3ca7a68c1a32..3ce77dca89599 100644 --- a/ee/api/feature_flag_role_access.py +++ b/ee/api/feature_flag_role_access.py @@ -35,7 +35,11 @@ def has_permission(self, request, view): return True try: feature_flag: FeatureFlag = FeatureFlag.objects.get(id=view.parents_query_dict["feature_flag_id"]) - if feature_flag.created_by.uuid == request.user.uuid: + if ( + hasattr(feature_flag, "created_by") + and feature_flag.created_by + and feature_flag.created_by.uuid == request.user.uuid + ): return True except FeatureFlag.DoesNotExist: raise exceptions.NotFound("Feature flag not found.") diff --git a/ee/api/test/test_billing.py b/ee/api/test/test_billing.py index 88addd2d7f416..c37c3ee9d6482 100644 --- a/ee/api/test/test_billing.py +++ b/ee/api/test/test_billing.py @@ -2,9 +2,9 @@ from typing import Any, Dict, List from unittest.mock import MagicMock, patch from uuid import uuid4 +from zoneinfo import ZoneInfo import jwt -from zoneinfo import ZoneInfo from dateutil.relativedelta import relativedelta from django.utils.timezone import now from freezegun import freeze_time diff --git a/ee/api/test/test_feature_flag_role_access.py b/ee/api/test/test_feature_flag_role_access.py index f143f10505f0f..3cd4e947d90c9 100644 --- a/ee/api/test/test_feature_flag_role_access.py +++ b/ee/api/test/test_feature_flag_role_access.py @@ -37,6 +37,26 @@ def test_can_always_add_role_access_if_creator_of_feature_flag(self): self.assertEqual(flag_role.role.name, self.eng_role.name) self.assertEqual(flag_role.feature_flag.id, self.feature_flag.id) + def test_role_access_with_deleted_creator_of_feature_flag(self): + OrganizationResourceAccess.objects.create( + resource=OrganizationResourceAccess.Resources.FEATURE_FLAGS, + access_level=OrganizationResourceAccess.AccessLevel.CAN_ONLY_VIEW, + organization=self.organization, + ) + + flag = FeatureFlag.objects.create( + created_by=None, + team=self.team, + key="flag_role_access_none", + name="Flag role access", + ) + self.assertEqual(self.user.role_memberships.count(), 0) + flag_role_access_create_res = self.client.post( + f"/api/projects/@current/feature_flags/{flag.id}/role_access", + {"role_id": self.eng_role.id}, + ) + self.assertEqual(flag_role_access_create_res.status_code, status.HTTP_403_FORBIDDEN) + def test_cannot_add_role_access_if_feature_flags_access_level_too_low_and_not_creator(self): OrganizationResourceAccess.objects.create( resource=OrganizationResourceAccess.Resources.FEATURE_FLAGS, diff --git a/ee/api/test/test_organization.py b/ee/api/test/test_organization.py index 2f1b11bb95256..a77361dc579e8 100644 --- a/ee/api/test/test_organization.py +++ b/ee/api/test/test_organization.py @@ -1,6 +1,7 @@ import datetime as dt import random -from unittest.mock import ANY, patch +from unittest import mock +from unittest.mock import ANY, call, patch from freezegun.api import freeze_time from rest_framework import status @@ -104,11 +105,21 @@ def test_delete_last_organization(self, mock_capture): "Did not return a 404 on trying to delete a nonexistent org", ) - mock_capture.assert_called_once_with( - self.user.distinct_id, - "organization deleted", - organization_props, - groups={"instance": ANY, "organization": str(org_id)}, + mock_capture.assert_has_calls( + [ + call( + self.user.distinct_id, + "membership level changed", + properties={"new_level": 15, "previous_level": 1}, + groups=mock.ANY, + ), + call( + self.user.distinct_id, + "organization deleted", + organization_props, + groups={"instance": mock.ANY, "organization": str(org_id)}, + ), + ] ) def test_no_delete_organization_not_owning(self): diff --git a/ee/billing/billing_manager.py b/ee/billing/billing_manager.py index 5a8119c57df9b..324b158fe071d 100644 --- a/ee/billing/billing_manager.py +++ b/ee/billing/billing_manager.py @@ -6,6 +6,7 @@ import structlog from django.utils import timezone from rest_framework.exceptions import NotAuthenticated +from sentry_sdk import capture_exception from ee.billing.billing_types import BillingStatus from ee.billing.quota_limiting import set_org_usage_summary, sync_org_quota_limits @@ -13,7 +14,7 @@ from ee.settings import BILLING_SERVICE_URL from posthog.cloud_utils import get_cached_instance_license from posthog.models import Organization -from posthog.models.organization import OrganizationUsageInfo +from posthog.models.organization import OrganizationMembership, OrganizationUsageInfo logger = structlog.get_logger(__name__) @@ -114,6 +115,14 @@ def update_billing_distinct_ids(self, organization: Organization) -> None: distinct_ids = list(organization.members.values_list("distinct_id", flat=True)) self.update_billing(organization, {"distinct_ids": distinct_ids}) + def update_billing_customer_email(self, organization: Organization) -> None: + try: + owner_membership = OrganizationMembership.objects.get(organization=organization, level=15) + user = owner_membership.user + self.update_billing(organization, {"org_customer_email": user.email}) + except Exception as e: + capture_exception(e) + def deactivate_products(self, organization: Organization, products: str) -> None: res = requests.get( f"{BILLING_SERVICE_URL}/api/billing/deactivate?products={products}", diff --git a/ee/billing/test/test_billing_manager.py b/ee/billing/test/test_billing_manager.py index e0c09e0d071fb..1dbbcb464f068 100644 --- a/ee/billing/test/test_billing_manager.py +++ b/ee/billing/test/test_billing_manager.py @@ -33,3 +33,26 @@ def test_update_billing_distinct_ids(self, billing_patch_request_mock: MagicMock BillingManager(license).update_billing_distinct_ids(organization) assert billing_patch_request_mock.call_count == 1 assert len(billing_patch_request_mock.call_args[1]["json"]["distinct_ids"]) == 2 + + @patch( + "ee.billing.billing_manager.requests.patch", + return_value=MagicMock(status_code=200, json=MagicMock(return_value={"text": "ok"})), + ) + def test_update_billing_customer_email(self, billing_patch_request_mock: MagicMock): + organization = self.organization + license = super(LicenseManager, cast(LicenseManager, License.objects)).create( + key="key123::key123", + plan="enterprise", + valid_until=timezone.datetime(2038, 1, 19, 3, 14, 7), + ) + User.objects.create_and_join( + organization=organization, + email="y@x.com", + password=None, + level=OrganizationMembership.Level.OWNER, + ) + organization.refresh_from_db() + assert len(organization.members.values_list("distinct_id", flat=True)) == 2 # one exists in the test base + BillingManager(license).update_billing_customer_email(organization) + assert billing_patch_request_mock.call_count == 1 + assert billing_patch_request_mock.call_args[1]["json"]["org_customer_email"] == "y@x.com" diff --git a/ee/clickhouse/views/test/__snapshots__/test_clickhouse_experiment_secondary_results.ambr b/ee/clickhouse/views/test/__snapshots__/test_clickhouse_experiment_secondary_results.ambr index 262c4a8e1e195..3474ae77b858f 100644 --- a/ee/clickhouse/views/test/__snapshots__/test_clickhouse_experiment_secondary_results.ambr +++ b/ee/clickhouse/views/test/__snapshots__/test_clickhouse_experiment_secondary_results.ambr @@ -1,6 +1,6 @@ # name: ClickhouseTestExperimentSecondaryResults.test_basic_secondary_metric_results ' - /* user_id:132 celery:posthog.celery.sync_insight_caching_state */ + /* user_id:131 celery:posthog.celery.sync_insight_caching_state */ SELECT team_id, date_diff('second', max(timestamp), now()) AS age FROM events diff --git a/frontend/__snapshots__/components-command-bar--actions.png b/frontend/__snapshots__/components-command-bar--actions.png new file mode 100644 index 0000000000000..a1c3b448c6a2e Binary files /dev/null and b/frontend/__snapshots__/components-command-bar--actions.png differ diff --git a/frontend/__snapshots__/components-command-bar--search.png b/frontend/__snapshots__/components-command-bar--search.png new file mode 100644 index 0000000000000..d155d92fac149 Binary files /dev/null and b/frontend/__snapshots__/components-command-bar--search.png differ diff --git a/frontend/__snapshots__/components-command-bar--shortcuts.png b/frontend/__snapshots__/components-command-bar--shortcuts.png new file mode 100644 index 0000000000000..32f4e7ff34955 Binary files /dev/null and b/frontend/__snapshots__/components-command-bar--shortcuts.png differ diff --git a/frontend/__snapshots__/components-networkrequesttiming--basic.png b/frontend/__snapshots__/components-networkrequesttiming--basic.png index 8247b433f71c8..effc91c21a0a6 100644 Binary files a/frontend/__snapshots__/components-networkrequesttiming--basic.png and b/frontend/__snapshots__/components-networkrequesttiming--basic.png differ diff --git a/frontend/__snapshots__/layout-navigation--app-page-with-side-bar-shown.png b/frontend/__snapshots__/layout-navigation--app-page-with-side-bar-shown.png index b49b6fc4bd341..b2ae49cd91f5b 100644 Binary files a/frontend/__snapshots__/layout-navigation--app-page-with-side-bar-shown.png and b/frontend/__snapshots__/layout-navigation--app-page-with-side-bar-shown.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-banner--closable.png b/frontend/__snapshots__/lemon-ui-lemon-banner--closable.png index a05dd78b3e3e7..a7a8ac55c5061 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-banner--closable.png and b/frontend/__snapshots__/lemon-ui-lemon-banner--closable.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-banner--dismissable.png b/frontend/__snapshots__/lemon-ui-lemon-banner--dismissable.png index be2ef2e5a884b..540a8a3ef2c39 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-banner--dismissable.png and b/frontend/__snapshots__/lemon-ui-lemon-banner--dismissable.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-banner--error.png b/frontend/__snapshots__/lemon-ui-lemon-banner--error.png index 7db8c557495b9..9389cfa4ea1b2 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-banner--error.png and b/frontend/__snapshots__/lemon-ui-lemon-banner--error.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-banner--info.png b/frontend/__snapshots__/lemon-ui-lemon-banner--info.png index 7c6e78d57caf2..6848c05f89a32 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-banner--info.png and b/frontend/__snapshots__/lemon-ui-lemon-banner--info.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-banner--success.png b/frontend/__snapshots__/lemon-ui-lemon-banner--success.png index 2053ce5ccc6de..f3b58cb98363a 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-banner--success.png and b/frontend/__snapshots__/lemon-ui-lemon-banner--success.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-banner--warning.png b/frontend/__snapshots__/lemon-ui-lemon-banner--warning.png index bf8c975d7385b..3c41933fb5078 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-banner--warning.png and b/frontend/__snapshots__/lemon-ui-lemon-banner--warning.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-button--as-links.png b/frontend/__snapshots__/lemon-ui-lemon-button--as-links.png index 24ae6fe59d181..292f9ce7d0a99 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-button--as-links.png and b/frontend/__snapshots__/lemon-ui-lemon-button--as-links.png differ diff --git a/frontend/__snapshots__/posthog-3000-keyboard-shortcut--default.png b/frontend/__snapshots__/posthog-3000-keyboard-shortcut--default.png new file mode 100644 index 0000000000000..995c1bc753ad1 Binary files /dev/null and b/frontend/__snapshots__/posthog-3000-keyboard-shortcut--default.png differ diff --git a/frontend/__snapshots__/posthog-3000-keyboard-shortcut--muted.png b/frontend/__snapshots__/posthog-3000-keyboard-shortcut--muted.png new file mode 100644 index 0000000000000..995c1bc753ad1 Binary files /dev/null and b/frontend/__snapshots__/posthog-3000-keyboard-shortcut--muted.png differ diff --git a/frontend/__snapshots__/posthog-3000-navigation--navigation-3000.png b/frontend/__snapshots__/posthog-3000-navigation--navigation-3000.png index 314ba79e762b7..bf6d08b1cb083 100644 Binary files a/frontend/__snapshots__/posthog-3000-navigation--navigation-3000.png and b/frontend/__snapshots__/posthog-3000-navigation--navigation-3000.png differ diff --git a/frontend/__snapshots__/posthog-3000-navigation--navigation-base.png b/frontend/__snapshots__/posthog-3000-navigation--navigation-base.png index c6353784ceaf1..87cd957353f8c 100644 Binary files a/frontend/__snapshots__/posthog-3000-navigation--navigation-base.png and b/frontend/__snapshots__/posthog-3000-navigation--navigation-base.png differ diff --git a/frontend/__snapshots__/scenes-app-batchexports--create-export.png b/frontend/__snapshots__/scenes-app-batchexports--create-export.png index 5812443d7cc01..51889a6cdcc34 100644 Binary files a/frontend/__snapshots__/scenes-app-batchexports--create-export.png and b/frontend/__snapshots__/scenes-app-batchexports--create-export.png differ diff --git a/frontend/__snapshots__/scenes-app-experiments--complete-funnel-experiment.png b/frontend/__snapshots__/scenes-app-experiments--complete-funnel-experiment.png index 189dc9741ea0f..0c9084824591a 100644 Binary files a/frontend/__snapshots__/scenes-app-experiments--complete-funnel-experiment.png and b/frontend/__snapshots__/scenes-app-experiments--complete-funnel-experiment.png differ diff --git a/frontend/__snapshots__/scenes-app-experiments--running-trend-experiment.png b/frontend/__snapshots__/scenes-app-experiments--running-trend-experiment.png index 60c850a72b2db..766de1662f8ae 100644 Binary files a/frontend/__snapshots__/scenes-app-experiments--running-trend-experiment.png and b/frontend/__snapshots__/scenes-app-experiments--running-trend-experiment.png differ diff --git a/frontend/__snapshots__/scenes-app-feature-flags--edit-feature-flag.png b/frontend/__snapshots__/scenes-app-feature-flags--edit-feature-flag.png index 0d7d6dd72c02b..9fe8b83975eb5 100644 Binary files a/frontend/__snapshots__/scenes-app-feature-flags--edit-feature-flag.png and b/frontend/__snapshots__/scenes-app-feature-flags--edit-feature-flag.png differ diff --git a/frontend/__snapshots__/scenes-app-insights--funnel-left-to-right-breakdown-edit.png b/frontend/__snapshots__/scenes-app-insights--funnel-left-to-right-breakdown-edit.png index 7ef2484ddde67..8be9ffc926019 100644 Binary files a/frontend/__snapshots__/scenes-app-insights--funnel-left-to-right-breakdown-edit.png and b/frontend/__snapshots__/scenes-app-insights--funnel-left-to-right-breakdown-edit.png differ diff --git a/frontend/__snapshots__/scenes-app-notebooks--bullet-list.png b/frontend/__snapshots__/scenes-app-notebooks--bullet-list.png index d196998a7e56f..2b56395785ef6 100644 Binary files a/frontend/__snapshots__/scenes-app-notebooks--bullet-list.png and b/frontend/__snapshots__/scenes-app-notebooks--bullet-list.png differ diff --git a/frontend/__snapshots__/scenes-app-notebooks--empty-notebook.png b/frontend/__snapshots__/scenes-app-notebooks--empty-notebook.png index 2086ed0aa90aa..c6df20187e100 100644 Binary files a/frontend/__snapshots__/scenes-app-notebooks--empty-notebook.png and b/frontend/__snapshots__/scenes-app-notebooks--empty-notebook.png differ diff --git a/frontend/__snapshots__/scenes-app-notebooks--headings.png b/frontend/__snapshots__/scenes-app-notebooks--headings.png index 3186fed28fd49..1d202bf688da1 100644 Binary files a/frontend/__snapshots__/scenes-app-notebooks--headings.png and b/frontend/__snapshots__/scenes-app-notebooks--headings.png differ diff --git a/frontend/__snapshots__/scenes-app-notebooks--notebook-not-found.png b/frontend/__snapshots__/scenes-app-notebooks--notebook-not-found.png index 0df1f64e9ec3c..6286e7ae27078 100644 Binary files a/frontend/__snapshots__/scenes-app-notebooks--notebook-not-found.png and b/frontend/__snapshots__/scenes-app-notebooks--notebook-not-found.png differ diff --git a/frontend/__snapshots__/scenes-app-notebooks--notebooks-list.png b/frontend/__snapshots__/scenes-app-notebooks--notebooks-list.png index c9f29c566c2c6..04209a125f40b 100644 Binary files a/frontend/__snapshots__/scenes-app-notebooks--notebooks-list.png and b/frontend/__snapshots__/scenes-app-notebooks--notebooks-list.png differ diff --git a/frontend/__snapshots__/scenes-app-notebooks--numbered-list.png b/frontend/__snapshots__/scenes-app-notebooks--numbered-list.png index cbe5aefd199ed..de7c9e016a3d0 100644 Binary files a/frontend/__snapshots__/scenes-app-notebooks--numbered-list.png and b/frontend/__snapshots__/scenes-app-notebooks--numbered-list.png differ diff --git a/frontend/__snapshots__/scenes-app-notebooks--text-formats.png b/frontend/__snapshots__/scenes-app-notebooks--text-formats.png index aa872f0a5cfe9..6f7c0b4c36de0 100644 Binary files a/frontend/__snapshots__/scenes-app-notebooks--text-formats.png and b/frontend/__snapshots__/scenes-app-notebooks--text-formats.png differ diff --git a/frontend/__snapshots__/scenes-app-notebooks--text-only-notebook.png b/frontend/__snapshots__/scenes-app-notebooks--text-only-notebook.png index f0ea3f9550997..c475638688418 100644 Binary files a/frontend/__snapshots__/scenes-app-notebooks--text-only-notebook.png and b/frontend/__snapshots__/scenes-app-notebooks--text-only-notebook.png differ diff --git a/frontend/__snapshots__/scenes-app-pipeline--pipeline-app-logs.png b/frontend/__snapshots__/scenes-app-pipeline--pipeline-app-logs.png index f7ea3ec3cff02..fc3f054686e53 100644 Binary files a/frontend/__snapshots__/scenes-app-pipeline--pipeline-app-logs.png and b/frontend/__snapshots__/scenes-app-pipeline--pipeline-app-logs.png differ diff --git a/frontend/__snapshots__/scenes-app-surveys--surveys-list.png b/frontend/__snapshots__/scenes-app-surveys--surveys-list.png index 012692ee2758d..80ccaa5e006fd 100644 Binary files a/frontend/__snapshots__/scenes-app-surveys--surveys-list.png and b/frontend/__snapshots__/scenes-app-surveys--surveys-list.png differ diff --git a/frontend/__snapshots__/scenes-other-login--sso-error.png b/frontend/__snapshots__/scenes-other-login--sso-error.png index 4bac52c291407..37309681cfb75 100644 Binary files a/frontend/__snapshots__/scenes-other-login--sso-error.png and b/frontend/__snapshots__/scenes-other-login--sso-error.png differ diff --git a/frontend/__snapshots__/scenes-other-settings--settings-organization.png b/frontend/__snapshots__/scenes-other-settings--settings-organization.png index 240b6b4c957e5..0e5752e576d8b 100644 Binary files a/frontend/__snapshots__/scenes-other-settings--settings-organization.png and b/frontend/__snapshots__/scenes-other-settings--settings-organization.png differ diff --git a/frontend/__snapshots__/scenes-other-settings--settings-project.png b/frontend/__snapshots__/scenes-other-settings--settings-project.png index 71514ddb64f12..227e1bac41f89 100644 Binary files a/frontend/__snapshots__/scenes-other-settings--settings-project.png and b/frontend/__snapshots__/scenes-other-settings--settings-project.png differ diff --git a/frontend/__snapshots__/scenes-other-settings--settings-user.png b/frontend/__snapshots__/scenes-other-settings--settings-user.png index 5574601d11794..7d18a6db46dd5 100644 Binary files a/frontend/__snapshots__/scenes-other-settings--settings-user.png and b/frontend/__snapshots__/scenes-other-settings--settings-user.png differ diff --git a/frontend/src/layout/navigation-3000/components/Breadcrumbs.scss b/frontend/src/layout/navigation-3000/components/Breadcrumbs.scss index c143a10085e9a..0cf4e5a260384 100644 --- a/frontend/src/layout/navigation-3000/components/Breadcrumbs.scss +++ b/frontend/src/layout/navigation-3000/components/Breadcrumbs.scss @@ -54,6 +54,7 @@ overflow: hidden; height: calc(1.2em * (1 - var(--breadcrumbs-compaction-rate))); box-sizing: content-box; + font-family: var(--font-sans) !important; > * { position: absolute; diff --git a/frontend/src/layout/navigation-3000/components/KeyboardShortcut.scss b/frontend/src/layout/navigation-3000/components/KeyboardShortcut.scss index 6a1645dbcfd0e..1620ab43cb0ab 100644 --- a/frontend/src/layout/navigation-3000/components/KeyboardShortcut.scss +++ b/frontend/src/layout/navigation-3000/components/KeyboardShortcut.scss @@ -3,18 +3,27 @@ } .KeyboardShortcut__key { - display: inline-flex; align-items: center; - justify-content: center; - height: 1.25rem; - min-width: 1.25rem; - padding: 0 0.1875rem; + background: var(--accent-3000); border-radius: 0.125rem; border-width: 1px; - background: var(--accent-3000); color: var(--default); + display: inline-flex; + height: 1.25rem; + justify-content: center; + min-width: 1.25rem; + padding: 0 0.1875rem; text-transform: capitalize; + .posthog-3000 & { + border-bottom-width: 2px; + border-color: var(--secondary-3000-button-border-hover); + border-radius: 0.25rem; + font-size: 0.625rem; + padding: 0.125rem 0.25rem; + text-transform: uppercase; + } + .KeyboardShortcut--muted > & { background: none; color: var(--muted); diff --git a/frontend/src/layout/navigation-3000/components/KeyboardShortcut.stories.tsx b/frontend/src/layout/navigation-3000/components/KeyboardShortcut.stories.tsx new file mode 100644 index 0000000000000..435e87ebfbdce --- /dev/null +++ b/frontend/src/layout/navigation-3000/components/KeyboardShortcut.stories.tsx @@ -0,0 +1,27 @@ +import { Meta } from '@storybook/react' + +import { KeyboardShortcut } from './KeyboardShortcut' + +const meta: Meta = { + title: 'PostHog 3000/Keyboard Shortcut', + component: KeyboardShortcut, + tags: ['autodocs'], +} +export default meta + +export const Default = { + args: { + cmd: true, + shift: true, + k: true, + }, +} + +export const Muted = { + args: { + muted: true, + cmd: true, + shift: true, + k: true, + }, +} diff --git a/frontend/src/layout/navigation-3000/components/KeyboardShortcut.tsx b/frontend/src/layout/navigation-3000/components/KeyboardShortcut.tsx index 7dd3dcceaa01c..d6d5b42d7a91d 100644 --- a/frontend/src/layout/navigation-3000/components/KeyboardShortcut.tsx +++ b/frontend/src/layout/navigation-3000/components/KeyboardShortcut.tsx @@ -36,11 +36,11 @@ export function KeyboardShortcut({ muted, ...keys }: KeyboardShortcutProps): JSX ) as HotKeyOrModifier[] return ( - + {sortedKeys.map((key) => ( - - {KEY_TO_SYMBOL[key] || key} - + {KEY_TO_SYMBOL[key] || key} ))} ) diff --git a/frontend/src/layout/navigation-3000/components/Navbar.tsx b/frontend/src/layout/navigation-3000/components/Navbar.tsx index a4799bd7da326..33f73e869b6fa 100644 --- a/frontend/src/layout/navigation-3000/components/Navbar.tsx +++ b/frontend/src/layout/navigation-3000/components/Navbar.tsx @@ -1,7 +1,9 @@ -import { IconAsterisk, IconDay, IconGear, IconNight } from '@posthog/icons' +import { IconAsterisk, IconDay, IconGear, IconNight, IconSearch } from '@posthog/icons' import { LemonBadge } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' +import { commandBarLogic } from 'lib/components/CommandBar/commandBarLogic' import { Resizer } from 'lib/components/Resizer/Resizer' +import { useFeatureFlag } from 'lib/hooks/useFeatureFlag' import { Popover } from 'lib/lemon-ui/Popover' import { ProfilePicture } from 'lib/lemon-ui/ProfilePicture' import { featureFlagLogic } from 'lib/logic/featureFlagLogic' @@ -12,25 +14,40 @@ import { userLogic } from 'scenes/userLogic' import { navigationLogic } from '~/layout/navigation/navigationLogic' import { SitePopoverOverlay } from '~/layout/navigation/TopBar/SitePopover' +import { KeyboardShortcut } from '~/layout/navigation-3000/components/KeyboardShortcut' import { navigation3000Logic } from '../navigationLogic' import { themeLogic } from '../themeLogic' import { NavbarButton } from './NavbarButton' +export function ThemeIcon(): JSX.Element { + const { isDarkModeOn, isThemeSyncedWithSystem } = useValues(themeLogic) + + const activeThemeIcon = isDarkModeOn ? : + + return isThemeSyncedWithSystem ? ( +
+ {activeThemeIcon} + } /> +
+ ) : ( + activeThemeIcon + ) +} + export function Navbar(): JSX.Element { const { user } = useValues(userLogic) const { isSitePopoverOpen } = useValues(navigationLogic) const { closeSitePopover, toggleSitePopover } = useActions(navigationLogic) const { isSidebarShown, activeNavbarItemId, navbarItems } = useValues(navigation3000Logic) const { showSidebar, hideSidebar, toggleNavCollapsed } = useActions(navigation3000Logic) - const { isDarkModeOn, darkModeSavedPreference, darkModeSystemPreference, isThemeSyncedWithSystem } = - useValues(themeLogic) + const { darkModeSavedPreference, darkModeSystemPreference } = useValues(themeLogic) const { toggleTheme } = useActions(themeLogic) const { featureFlags } = useValues(featureFlagLogic) - - const activeThemeIcon = isDarkModeOn ? : + const { toggleSearchBar } = useActions(commandBarLogic) const containerRef = useRef(null) + const isUsingNewNav = useFeatureFlag('POSTHOG_3000_NAV') return (