Radish alpha
r
rad:z4V1sjrXqjvFdnCUbxPFqd5p4DtH5
Radicle web interface
Radicle
Git
Various design fixes for BranchSelector
Merged did:key:z6MkkfM3...sVz5 opened 2 years ago
  • Add Canonical badge for canonical branch
  • Add commit summary to BranchSelector
  • Add branch icon for each branch in the list

check check-visual check-unit-test check-httpd-api-unit-test check-e2e check-build

👉 Preview
👉 Workflow runs
👉 Branch on GitHub

8 files changed +121 -92 893af0c4 2d3fbe79
modified src/components/Badge.svelte
@@ -9,6 +9,7 @@
    | "negative"
    | "positive"
    | "primary"
+
    | "foreground-emphasized"
    | "delegate"
    | "secondary";
  export let round: boolean = false;
@@ -39,6 +40,10 @@
    color: var(--color-foreground-match-background);
    background: var(--color-fill-gray);
  }
+
  .foreground-emphasized {
+
    background-color: var(--color-fill-counter-emphasized);
+
    color: var(--color-foreground-emphasized);
+
  }
  .delegate {
    color: var(--color-foreground-primary);
    background: var(--color-fill-delegate);
@@ -119,6 +124,7 @@
  class:yellow={variant === "yellow"}
  class:delegate={variant === "delegate"}
  class:outline={variant === "outline"}
+
  class:foreground-emphasized={variant === "foreground-emphasized"}
  class:background={variant === "background"}
  class:foreground={variant === "foreground"}
  class:neutral={variant === "neutral"}
modified src/views/projects/Source/Blob.svelte
@@ -10,11 +10,10 @@
  import { routeToPath } from "@app/lib/router";

  import Button from "@app/components/Button.svelte";
+
  import CommitButton from "@app/views/projects/components/CommitButton.svelte";
  import File from "@app/components/File.svelte";
  import FilePath from "@app/components/FilePath.svelte";
  import IconSmall from "@app/components/IconSmall.svelte";
-
  import InlineMarkdown from "@app/components/InlineMarkdown.svelte";
-
  import Link from "@app/components/Link.svelte";
  import Markdown from "@app/components/Markdown.svelte";
  import Placeholder from "@app/components/Placeholder.svelte";
  import Radio from "@app/components/Radio.svelte";
@@ -166,41 +165,12 @@
  .no-scrollbar::-webkit-scrollbar {
    display: none;
  }
-
  .commit-teaser {
-
    display: flex;
-
    align-items: center;
-
    overflow: hidden;
-
    border-radius: var(--border-radius-tiny);
-
    background-color: var(--color-fill-ghost-hover);
-
    gap: 0.75rem;
-
    padding-right: 0.75rem;
-
    height: var(--button-small-height);
-
  }
</style>

<File sticky={false}>
  <FilePath slot="left-header" filenameWithPath={blob.path} />
  <svelte:fragment slot="right-header">
-
    <div class="commit-teaser">
-
      <div class="hash-button">
-
        <Link
-
          route={{
-
            resource: "project.commit",
-
            project: projectId,
-
            node: baseUrl,
-
            commit: lastCommit.id,
-
          }}>
-
          <Button variant="gray" styleBorderRadius="0">
-
            <span class="global-commit">
-
              {lastCommit.id.slice(0, 7)}
-
            </span>
-
          </Button>
-
        </Link>
-
      </div>
-
      <div style:white-space="nowrap">
-
        <InlineMarkdown fontSize="small" content={lastCommit.summary} />
-
      </div>
-
    </div>
+
    <CommitButton styleRoundBorders {projectId} {baseUrl} commit={lastCommit} />
    <div class="global-hide-on-mobile teaser-buttons">
      {#if enablePreview}
        <Radio ariaLabel="Toggle render method">
modified src/views/projects/Source/BranchSelector.svelte
@@ -1,25 +1,25 @@
<script lang="ts">
-
  import type { BaseUrl, Project } from "@httpd-client";
+
  import type { BaseUrl, Commit, Project } from "@httpd-client";
  import type { Route } from "@app/lib/router";

-
  import * as utils from "@app/lib/utils";
  import { activeUnloadedRouteStore } from "@app/lib/router";
  import { closeFocused } from "@app/components/Popover.svelte";

+
  import Badge from "@app/components/Badge.svelte";
+
  import Button from "@app/components/Button.svelte";
+
  import CommitButton from "@app/views/projects/components/CommitButton.svelte";
  import DropdownList from "@app/components/DropdownList.svelte";
  import DropdownListItem from "@app/components/DropdownList/DropdownListItem.svelte";
-
  import Popover from "@app/components/Popover.svelte";
  import IconSmall from "@app/components/IconSmall.svelte";
  import Link from "@app/components/Link.svelte";
-
  import Button from "@app/components/Button.svelte";
+
  import Popover from "@app/components/Popover.svelte";

+
  export let onCanonical: boolean;
  export let branches: Array<{ name: string; route: Route }>;
  export let node: BaseUrl;
  export let project: Project;
  export let selectedBranch: string | undefined;
-
  export let selectedCommitId: string;
-

-
  $: selectedCommitShortId = utils.formatCommit(selectedCommitId);
+
  export let selectedCommit: Commit["commit"];
</script>

<style>
@@ -41,17 +41,22 @@
    <Popover
      popoverPadding="0"
      popoverPositionTop="2.5rem"
-
      popoverBorderRadius="var(--border-radius-small)">
+
      popoverBorderRadius="var(--border-radius-tiny)">
      <Button
        variant="gray-white"
        let:expanded
        let:toggle
        on:click={toggle}
        slot="toggle"
-
        styleBorderRadius="var(--border-radius-small) 0 0 var(--border-radius-small)"
+
        styleBorderRadius="var(--border-radius-tiny) 0 0 var(--border-radius-tiny)"
        title="Change branch">
        <IconSmall name="branch" />
        <div class="identifier">{selectedBranch}</div>
+
        {#if onCanonical}
+
          <Badge title="Canonical branch" variant="foreground-emphasized">
+
            Canonical
+
          </Badge>
+
        {/if}
        <IconSmall name={expanded ? "chevron-up" : "chevron-down"} />
      </Button>

@@ -62,7 +67,15 @@
        <svelte:fragment slot="item" let:item>
          <Link route={item.route} on:afterNavigate={() => closeFocused()}>
            <DropdownListItem selected={item.name === selectedBranch}>
-
              <div class="identifier">{item.name}</div>
+
              <IconSmall name="branch" />
+
              <div class="identifier">
+
                {item.name}
+
              </div>
+
              {#if onCanonical}
+
                <Badge title="Canonical branch" variant="foreground-emphasized">
+
                  Canonical
+
                </Badge>
+
              {/if}
            </DropdownListItem>
          </Link>
        </svelte:fragment>
@@ -71,7 +84,10 @@
            route={$activeUnloadedRouteStore}
            on:afterNavigate={() => closeFocused()}>
            <DropdownListItem selected>
-
              <div class="identifier">{project.defaultBranch}</div>
+
              <IconSmall name="branch" />
+
              <div class="identifier">
+
                {project.defaultBranch}
+
              </div>
            </DropdownListItem>
          </Link>
        </svelte:fragment>
@@ -80,27 +96,5 @@
  {/if}

  <div class="global-spacer" />
-

-
  <Button
-
    title="Current HEAD"
-
    variant="not-selected"
-
    styleBorderRadius={selectedBranch
-
      ? "0 var(--border-radius-small) var(--border-radius-small) 0"
-
      : "var(--border-radius-small)"}>
-
    <Link
-
      route={{
-
        resource: "project.commit",
-
        project: project.id,
-
        node,
-
        commit: selectedCommitId,
-
      }}>
-
      <div class="identifier global-commit">
-
        {#if !selectedBranch}
-
          <IconSmall name="branch" />
-
        {/if}
-

-
        {selectedCommitShortId}
-
      </div>
-
    </Link>
-
  </Button>
+
  <CommitButton projectId={project.id} commit={selectedCommit} baseUrl={node} />
</div>
modified src/views/projects/Source/Header.svelte
@@ -22,13 +22,13 @@

  // Revision may be a commit ID, a branch name or `undefined` which means the
  // default branch. We assign `selectedBranch` accordingly.
-
  $: if (revision === commitId) {
+
  $: if (revision === commit.id) {
    selectedBranch = undefined;
  } else {
    selectedBranch = revision || project.defaultBranch;
  }

-
  $: commitId = tree.lastCommit.id;
+
  $: commit = tree.lastCommit;
  $: peer = peers.find(p => p.selected)?.remote.id;
</script>

@@ -88,7 +88,8 @@
    {branches}
    {project}
    {node}
-
    selectedCommitId={commitId}
+
    onCanonical={Boolean(!peer && selectedBranch === project.defaultBranch)}
+
    selectedCommit={commit}
    {selectedBranch} />
</div>

added src/views/projects/components/CommitButton.svelte
@@ -0,0 +1,49 @@
+
<script lang="ts">
+
  import type { BaseUrl, Commit } from "@httpd-client";
+

+
  import Button from "@app/components/Button.svelte";
+
  import Link from "@app/components/Link.svelte";
+
  import { formatCommit } from "@app/lib/utils";
+

+
  export let styleRoundBorders: boolean = false;
+
  export let projectId: string;
+
  export let baseUrl: BaseUrl;
+
  export let commit: Commit["commit"];
+

+
  $: commitShortId = formatCommit(commit.id);
+
</script>
+

+
<style>
+
  .commit {
+
    display: flex;
+
    align-items: center;
+
    gap: 0.5rem;
+
  }
+
  .identifier {
+
    display: flex;
+
    align-items: center;
+
    gap: 0.5rem;
+
  }
+
</style>
+

+
<Button
+
  title="Current HEAD"
+
  variant="not-selected"
+
  styleBorderRadius={styleRoundBorders
+
    ? "var(--border-radius-tiny)"
+
    : "0 var(--border-radius-tiny) var(--border-radius-tiny) 0"}>
+
  <Link
+
    route={{
+
      resource: "project.commit",
+
      project: projectId,
+
      node: baseUrl,
+
      commit: commit.id,
+
    }}>
+
    <div class="commit">
+
      <div class="identifier global-commit">
+
        {commitShortId}
+
      </div>
+
      <span>{commit.summary}</span>
+
    </div>
+
  </Link>
+
</Button>
modified tests/e2e/project.spec.ts
@@ -1,6 +1,5 @@
import {
  aliceMainHead,
-
  bobHead,
  cobUrl,
  expect,
  markdownUrl,
@@ -29,9 +28,9 @@ test("navigate to project", async ({ page }) => {
  // Project menu shows default selected branch and commit and contributor counts.
  {
    await expect(page.getByTitle("Change branch")).toBeVisible();
-
    await expect(page.getByTitle("Current HEAD")).toHaveText(
-
      aliceMainHead.substring(0, 7),
-
    );
+
    await expect(
+
      page.getByRole("button", { name: "4a9f278 Add submodule" }).first(),
+
    ).toBeVisible();
    await expect(
      page.getByRole("link", {
        name: "Commits 7",
@@ -289,9 +288,9 @@ test("peer and branch switching", async ({ page }) => {
    // Default `main` branch.
    {
      await expect(page.getByTitle("Change branch")).toHaveText("main");
-
      await expect(page.getByTitle("Current HEAD")).toHaveText(
-
        aliceMainHead.substring(0, 7),
-
      );
+
      await expect(
+
        page.getByRole("button", { name: "4a9f278 Add submodule" }).first(),
+
      ).toBeVisible();
      await expect(
        page.getByRole("link", {
          name: "Commits 7",
@@ -307,7 +306,9 @@ test("peer and branch switching", async ({ page }) => {
      await expect(
        page.getByRole("button", { name: "feature/branch" }),
      ).toBeVisible();
-
      await expect(page.getByTitle("Current HEAD")).toHaveText("1aded56");
+
      await expect(
+
        page.getByRole("button", { name: "1aded56 Add subconscious file" }),
+
      ).toBeVisible();
      await expect(
        page.getByRole("link", {
          name: "Commits 9",
@@ -323,7 +324,9 @@ test("peer and branch switching", async ({ page }) => {
      await expect(
        page.getByRole("button", { name: "orphaned-branch" }),
      ).toBeVisible();
-
      await expect(page.getByTitle("Current HEAD")).toHaveText("af3641c");
+
      await expect(
+
        page.getByRole("button", { name: "af3641c Add empty orphaned" }),
+
      ).toBeVisible();
      await expect(
        page.getByRole("link", {
          name: "Commits 1",
@@ -342,9 +345,9 @@ test("peer and branch switching", async ({ page }) => {
    await expect(page.getByTitle("Change peer")).not.toContainText("bob");

    await expect(page.getByTitle("Change branch")).toBeVisible();
-
    await expect(page.getByTitle("Current HEAD")).toHaveText(
-
      aliceMainHead.substring(0, 7),
-
    );
+
    await expect(
+
      page.getByRole("button", { name: "4a9f278 Add submodule" }).first(),
+
    ).toBeVisible();
    await expect(page.getByText("Git test repository")).toBeVisible();
  }

@@ -358,16 +361,16 @@ test("peer and branch switching", async ({ page }) => {
    // Default `main` branch.
    {
      await expect(page.getByRole("button", { name: "main" })).toBeVisible();
-
      await expect(page.getByTitle("Current HEAD")).toHaveText(
-
        bobHead.substring(0, 7),
-
      );
+
      await expect(
+
        page.getByRole("button", { name: "ff32f18 Update readme" }).first(),
+
      ).toBeVisible();
      await expect(
        page.getByRole("link", {
          name: "Commits 8",
        }),
      ).toBeVisible();
      await expect(
-
        page.getByText(`${bobHead.substring(0, 7)} Update readme`),
+
        page.getByRole("button", { name: "ff32f18 Update readme" }).first(),
      ).toBeVisible();
    }
  }
modified tests/e2e/project/commit.spec.ts
@@ -15,7 +15,7 @@ test("navigation from commit list", async ({ page }) => {
  await page.getByRole("link", { name: "bob" }).click();
  await page.getByRole("link", { name: "Commits 8" }).click();

-
  await page.getByText("Update readme").click();
+
  await page.getByText("Update readme").first().click();
  await expect(page).toHaveURL(commitUrl);
});

@@ -113,13 +113,14 @@ test("navigation to source tree at specific revision", async ({ page }) => {

  // Go to source tree at this revision.
  await page.getByTitle("View file at this commit").click();
+
  const branchSelectorCommitButton = page.getByTitle("Current HEAD").first();
  await expect(
-
    page.getByText("Add a deeply nested directory tree"),
+
    branchSelectorCommitButton.getByText("Add a deeply nested directory tree"),
  ).toBeVisible();
  await expect(page).toHaveURL(
    `${sourceBrowsingUrl}/tree/0801aceeab500033f8d608778218657bd626ef73/deep/directory/hierarchy/is/entirely/possible/in/git/repositories/.gitkeep`,
  );
-
  await expect(page.getByTitle("Current HEAD")).toContainText("0801ace");
+
  await expect(branchSelectorCommitButton).toContainText("0801ace");
  await expect(page.locator(".source-tree >> text=.gitkeep")).toBeVisible();
  await expect(
    page
modified tests/e2e/project/commits.spec.ts
@@ -152,8 +152,9 @@ test("pushing changes while viewing history", async ({ page, peerManager }) => {
  await expect(page).toHaveURL(`${alice.uiUrl()}/${rid}/history`);
  await expect(page.getByRole("link", { name: "Commits 2" })).toBeVisible();

-
  await expect(page.getByTitle("Change branch")).toHaveText("main");
-
  await expect(page.getByTitle("Current HEAD")).toHaveText("516fa74");
+
  await expect(page.getByTitle("Change branch")).toHaveText("main Canonical");
+
  const branchSelectorCommitButton = page.getByTitle("Current HEAD").first();
+
  await expect(branchSelectorCommitButton).toHaveText("516fa74 first change");

  await page
    .getByRole("banner")
@@ -181,6 +182,10 @@ test("pushing changes while viewing history", async ({ page, peerManager }) => {
  await expect(page.getByRole("link", { name: "Commits 3" })).toHaveText(
    "Commits 3",
  );
-
  await expect(page.getByTitle("Change branch")).toHaveText("main");
-
  await expect(page.getByTitle("Current HEAD")).toHaveText("bb9089a");
+
  await expect(
+
    page.getByRole("button", { name: "main Canonical" }),
+
  ).toBeVisible();
+
  await expect(
+
    page.getByRole("link", { name: "bb9089a after clicking the" }),
+
  ).toBeVisible();
});