Skip to content

Commit

Permalink
Add video block (#61)
Browse files Browse the repository at this point in the history
* style: alphabetically sort and indenting

* feat: add video block

* chore: remove unused imports

* chore: reorder named arguments

* feat: add valid url property rule
  • Loading branch information
maartenpaauw authored Dec 17, 2024
1 parent 04bddbc commit ff33581
Show file tree
Hide file tree
Showing 7 changed files with 254 additions and 38 deletions.
129 changes: 129 additions & 0 deletions src/Blocks/Video.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
<?php

declare(strict_types=1);

namespace SlackPhp\BlockKit\Blocks;

use SlackPhp\BlockKit\Parts\PlainText;
use SlackPhp\BlockKit\Property;
use SlackPhp\BlockKit\Validation\RequiresAllOf;
use SlackPhp\BlockKit\Validation\ValidString;
use SlackPhp\BlockKit\Validation\ValidUrl;

#[RequiresAllOf('alt_text', 'title', 'thumbnail_url', 'video_url')]
class Video extends Block
{
#[Property, ValidString(200)]
public ?PlainText $title;

#[Property('video_url'), ValidUrl]
public ?string $videoUrl;

#[Property('thumbnail_url'), ValidUrl]
public ?string $thumbnailUrl;

#[Property('alt_text'), ValidString]
public ?string $altText;

#[Property, ValidString(200)]
public ?PlainText $description;

#[Property('author_name'), ValidString(50)]
public ?string $authorName;

#[Property('title_url'), ValidUrl]
public ?string $titleUrl;

#[Property('provider_name'), ValidString]
public ?string $providerName;

#[Property('provider_icon_url'), ValidUrl]
public ?string $providerIconUrl;

public function __construct(
PlainText|string|null $title = null,
?string $videoUrl = null,
?string $thumbnailUrl = null,
?string $altText = null,
PlainText|string|null $description = null,
?string $authorName = null,
?string $titleUrl = null,
?string $providerName = null,
?string $providerIconUrl = null,
?string $blockId = null,
) {
parent::__construct($blockId);
$this->title($title);
$this->videoUrl($videoUrl);
$this->thumbnailUrl($thumbnailUrl);
$this->altText($altText);
$this->description($description);
$this->authorName($authorName);
$this->titleUrl($titleUrl);
$this->providerName($providerName);
$this->providerIconUrl($providerIconUrl);
}

public function title(PlainText|string|null $title): self
{
$this->title = PlainText::wrap($title);

return $this;
}

public function videoUrl(?string $videoUrl): self
{
$this->videoUrl = $videoUrl;

return $this;
}

public function thumbnailUrl(?string $thumbnailUrl): self
{
$this->thumbnailUrl = $thumbnailUrl;

return $this;
}

public function altText(?string $altText): self
{
$this->altText = $altText;

return $this;
}

public function description(PlainText|string|null $description): self
{
$this->description = PlainText::wrap($description);

return $this;
}

public function authorName(?string $authorName): self
{
$this->authorName = $authorName;

return $this;
}

public function titleUrl(?string $titleUrl): self
{
$this->titleUrl = $titleUrl;

return $this;
}

public function providerName(?string $providerName): self
{
$this->providerName = $providerName;

return $this;
}

public function providerIconUrl(?string $providerIconUrl): self
{
$this->providerIconUrl = $providerIconUrl;

return $this;
}
}
20 changes: 15 additions & 5 deletions src/Kit.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,6 @@

namespace SlackPhp\BlockKit;

use SlackPhp\BlockKit\Collections;
use SlackPhp\BlockKit\Elements;
use SlackPhp\BlockKit\Parts;
use SlackPhp\BlockKit\Surfaces;

/**
* Kit acts as a static façade to the whole block kit library.
*/
Expand Down Expand Up @@ -156,6 +151,21 @@ public static function section(
): Blocks\Section {
return new Blocks\Section($text, $fields, $accessory, $blockId);
}

public static function video(
Parts\PlainText|string|null $title = null,
?string $videoUrl = null,
?string $thumbnailUrl = null,
?string $altText = null,
Parts\PlainText|string|null $description = null,
?string $authorName = null,
?string $titleUrl = null,
?string $providerName = null,
?string $providerIconUrl = null,
?string $blockId = null,
): Blocks\Video {
return new Blocks\Video($title, $videoUrl, $thumbnailUrl, $altText, $description, $authorName, $titleUrl, $providerName, $providerIconUrl, $blockId);
}
#endregion

#region Elements
Expand Down
48 changes: 25 additions & 23 deletions src/Type.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ enum Type: string
case ATTACHMENT = 'attachment';
case MESSAGE = 'message';
case MODAL = 'modal';
case WORKFLOW_STEP = 'workflow_step';
case OPTIONS_RESULT = 'options_result';
case WORKFLOW_STEP = 'workflow_step';

// Blocks
case ACTIONS = 'actions';
Expand All @@ -27,6 +27,7 @@ enum Type: string
case HEADER = 'header';
case INPUT = 'input';
case SECTION = 'section';
case VIDEO = 'video';

// Elements
case BUTTON = 'button';
Expand All @@ -38,16 +39,16 @@ enum Type: string
case MULTI_SELECT_EXTERNAL = 'multi_external_select';
case MULTI_SELECT_STATIC = 'multi_static_select';
case MULTI_SELECT_USERS = 'multi_users_select';
case NUMBER_INPUT = 'number_input';
case OVERFLOW_MENU = 'overflow';
case PLAIN_TEXT_INPUT = 'plain_text_input';
case RADIO_BUTTONS = 'radio_buttons';
case SELECT_CHANNELS = 'channels_select';
case SELECT_CONVERSATIONS = 'conversations_select';
case SELECT_EXTERNAL = 'external_select';
case SELECT_STATIC = 'static_select';
case SELECT_USERS = 'users_select';
case PLAIN_TEXT_INPUT = 'plain_text_input';
case TIMEPICKER = 'timepicker';
case NUMBER_INPUT = 'number_input';

// Parts (aka Composition Objects)
case CONFIRM = 'confirm';
Expand All @@ -66,8 +67,8 @@ enum Type: string
Surfaces\Attachment::class => self::ATTACHMENT,
Surfaces\Message::class => self::MESSAGE,
Surfaces\Modal::class => self::MODAL,
Surfaces\WorkflowStep::class => self::WORKFLOW_STEP,
Surfaces\OptionsResult::class => self::OPTIONS_RESULT,
Surfaces\WorkflowStep::class => self::WORKFLOW_STEP,

// Blocks
Blocks\Actions::class => self::ACTIONS,
Expand All @@ -78,33 +79,34 @@ enum Type: string
Blocks\Header::class => self::HEADER,
Blocks\Input::class => self::INPUT,
Blocks\Section::class => self::SECTION,
Blocks\Video::class => self::VIDEO,

// Virtual Blocks
Virtual\CodeBlock::class => self::SECTION,
Virtual\TwoColumnTable::class => self::SECTION,

// Elements
Elements\Button::class => self::BUTTON,
Elements\Checkboxes::class => self::CHECKBOXES,
Elements\DatePicker::class => self::DATEPICKER,
Elements\Image::class => self::IMAGE,
Elements\RadioButtons::class => self::RADIO_BUTTONS,
Elements\PlainTextInput::class => self::PLAIN_TEXT_INPUT,
Elements\TimePicker::class => self::TIMEPICKER,
Elements\NumberInput::class => self::NUMBER_INPUT,
Elements\Button::class => self::BUTTON,
Elements\Checkboxes::class => self::CHECKBOXES,
Elements\DatePicker::class => self::DATEPICKER,
Elements\Image::class => self::IMAGE,
Elements\NumberInput::class => self::NUMBER_INPUT,
Elements\OverflowMenu::class => self::OVERFLOW_MENU,
Elements\PlainTextInput::class => self::PLAIN_TEXT_INPUT,
Elements\RadioButtons::class => self::RADIO_BUTTONS,
Elements\TimePicker::class => self::TIMEPICKER,

// Menus
Elements\OverflowMenu::class => self::OVERFLOW_MENU,
Selects\MultiChannelSelectMenu::class => self::MULTI_SELECT_CHANNELS,
Selects\MultiConversationSelectMenu::class => self::MULTI_SELECT_CONVERSATIONS,
Selects\MultiExternalSelectMenu::class => self::MULTI_SELECT_EXTERNAL,
Selects\MultiStaticSelectMenu::class => self::MULTI_SELECT_STATIC,
Selects\MultiUserSelectMenu::class => self::MULTI_SELECT_USERS,
Selects\ChannelSelectMenu::class => self::SELECT_CHANNELS,
Selects\ConversationSelectMenu::class => self::SELECT_CONVERSATIONS,
Selects\ExternalSelectMenu::class => self::SELECT_EXTERNAL,
Selects\StaticSelectMenu::class => self::SELECT_STATIC,
Selects\UserSelectMenu::class => self::SELECT_USERS,
Selects\ChannelSelectMenu::class => self::SELECT_CHANNELS,
Selects\ConversationSelectMenu::class => self::SELECT_CONVERSATIONS,
Selects\ExternalSelectMenu::class => self::SELECT_EXTERNAL,
Selects\MultiChannelSelectMenu::class => self::MULTI_SELECT_CHANNELS,
Selects\MultiConversationSelectMenu::class => self::MULTI_SELECT_CONVERSATIONS,
Selects\MultiExternalSelectMenu::class => self::MULTI_SELECT_EXTERNAL,
Selects\MultiStaticSelectMenu::class => self::MULTI_SELECT_STATIC,
Selects\MultiUserSelectMenu::class => self::MULTI_SELECT_USERS,
Selects\StaticSelectMenu::class => self::SELECT_STATIC,
Selects\UserSelectMenu::class => self::SELECT_USERS,

// Parts (aka Composition Objects)
Parts\Confirm::class => self::CONFIRM,
Expand Down
26 changes: 26 additions & 0 deletions src/Validation/ValidUrl.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

namespace SlackPhp\BlockKit\Validation;

use Attribute;
use SlackPhp\BlockKit\Component;

#[Attribute(Attribute::TARGET_PROPERTY)]
class ValidUrl implements PropertyRule
{
public function check(Component $component, string $field, mixed $value): void
{
if ($value === null) {
return;
}

if (! \is_string($value) || filter_var($value, FILTER_VALIDATE_URL) === false) {
throw new ValidationException(
'The "%s" field of a valid "%s" component must be a valid URL',
[$field, $component->type->value],
);
}
}
}
32 changes: 32 additions & 0 deletions tests/Functional/BlockTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

namespace SlackPhp\BlockKit\Tests\Functional;

use SlackPhp\BlockKit\Blocks\Block;
use SlackPhp\BlockKit\Blocks\Video;

class BlockTest extends TestCase
{
/**
* @param class-string<Block> $class
* @dataProvider providesJsonFiles
*/
public function testMatchesJsonFromReferenceDocumentation(string $file, string $class): void
{
$json = $this->loadAssetJson($file);

$hydrate = [$class, 'fromJson'](...);
$block = $hydrate($json);
$block->validate();
$newJson = $block->toJson();

$this->assertJsonStringEqualsJsonString($json, $newJson);
}

public static function providesJsonFiles(): array
{
return [
['blocks/video', Video::class],
];
}
}
20 changes: 10 additions & 10 deletions tests/Functional/CreateTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@ class CreateTest extends TestCase
public function testCreatedComponentsMatchExpectedOutputJson(): void
{
$modal = Kit::modal(
title: 'My App',
submit: 'Submit',
close: 'Cancel',
blocks: [
Kit::input(
label: 'Choose a letter',
Expand All @@ -48,6 +45,9 @@ public function testCreatedComponentsMatchExpectedOutputJson(): void
),
)
],
title: 'My App',
submit: 'Submit',
close: 'Cancel',
);

$modal->validate();
Expand All @@ -66,21 +66,21 @@ public function testCreatedVirtualComponentsMatchExpectedOutputJson(): void
$message = Kit::message(
blocks: [
Kit::twoColumnTable(
blockId: 'foo',
cols: ['left', 'right'],
rows: [
['a', 'b'],
['c', 'd'],
],
cols: ['left', 'right'],
borders: true,
blockId: 'foo',
),
Kit::codeBlock(
blockId: 'bar',
caption: 'my-code.txt',
code: <<<CODE
This is
my code
CODE,
caption: 'my-code.txt',
blockId: 'bar',
),
Kit::codeBlock(
code: 'Code block without blockId'
Expand All @@ -97,13 +97,13 @@ public function testCreatedVirtualComponentsMatchExpectedOutputJson(): void
public function testCreateModalWithPrivateMetadata(): void
{
$modal = Kit::modal(
blocks: [
Kit::section('Hello, world!'),
],
title: 'My App',
privateMetadata: [
'foo' => 'bar',
],
blocks: [
Kit::section('Hello, world!'),
],
);

$modal->validate();
Expand Down
Loading

0 comments on commit ff33581

Please sign in to comment.